/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package androidx.media.filterfw; /** * A FrameType instance specifies the data format of a Frame. * * FrameTypes are used mainly by Filters to specify the data type they intend to consume or produce. * When filters are connected, their FrameType information is analyzed and checked for * compatibility. This allows Filter writers to assume a certain data input type. It also helps * filter-graph designers determine which filters can be hooked up to one another. * * A FrameType generally consists of an element type and number of dimensions. The currently * supported element types are: * * * * If the object element type is used, class information may be appended to the FrameType to * indicate what class of objects are expected. When constructing an object based FrameType, you * have the option of either specifying a type that represents a single object of that class, or * an array of objects (see the {@link #single()} and {@link #array()} constructors). A single * object has a dimensionality of 0, while an array has a dimensionality of 1. * * When constructing a non-object type, you have the option of creating a 1D or 2D buffer, or * a 2D image (see the {@link #buffer1D(int)}, {@link #buffer2D(int)}, and * {@link #image2D(int, int)} constructors). To optimize access, provide access hints when making * an image type. * * Finally, it is possible to create a wild-card type with the {@link #any()} constructor. This * type matches any other type. Note, that this is a more general type than a {@code single(Object)} * type that matches only object-base types (of any Object subclass). You may also specify the * leave the element of any type unspecified by using the {@code ELEMENT_DONTCARE} constant. * * When a graph is connected the types between outputs and inputs are merged to a queue-type. All * Frames in this queue will be of that type. In order for a merge to succeed the following * conditions must hold: * * * * Class compatibility is determined in an optimistic fashion, i.e. one class must be the subclass * of the other. It does not matter which of the types is the subclass of the other. For instance, * if one Filter outputs a type of class {@code Object}, and the consumer expects a Filter of type * {@code Bitmap}, the connection is considered compatible. (Of course if at runtime a non-Bitmap * object is produced, this will cause a runtime exception to be thrown). * * For convenience, single and array object-based types are compatible with one another. This * in turn means that Frames with a single object can be accessed as an array with a single entry, * and array based Frames can be accessed as a single object of the array class. For this reason * you should prefer consuming objects as array types (if it makes sense for that specific port), * as this will allow your Filter to handle multiple objects in one Frame while not giving up the * possibility to deal with singles. * TODO: This needs to be reworked. An array(int) should not be interchangeable with a single(int), * but rather with a single(int[]). Use ArraySelectFilter for the former! * * After the types are merged, the queue-type must be a fully specified type. This means that the * type must have its element and dimensions specified. This ensures that filters that need to * query their input or output types receive meaningful information. */ public final class FrameType { public final static int ELEMENT_DONTCARE = 0; public final static int ELEMENT_OBJECT = 1; public final static int ELEMENT_INT8 = 100; public final static int ELEMENT_INT16 = 101; public final static int ELEMENT_INT32 = 102; public final static int ELEMENT_INT64 = 103; public final static int ELEMENT_FLOAT32 = 200; public final static int ELEMENT_FLOAT64 = 201; public final static int ELEMENT_RGBA8888 = 301; public final static int READ_CPU = 0x01; public final static int READ_GPU = 0x02; public final static int READ_ALLOCATION = 0x04; public final static int WRITE_CPU = 0x08; public final static int WRITE_GPU = 0x10; public final static int WRITE_ALLOCATION = 0x20; private final static int ACCESS_UNKNOWN = 0x00; private final int mElementId; private final int mDimensions; private final int mAccessHints; private final Class mClass; private static SimpleCache mTypeCache = new SimpleCache(64); /** * Constructs a wild-card FrameType that matches any other FrameType. * @return The wild-card FrameType instance. */ public static FrameType any() { return FrameType.fetchType(ELEMENT_DONTCARE, -1, ACCESS_UNKNOWN); } /** * Constructs an object-based single FrameType that matches object-based FrameTypes of any * class. * @return A single object-based FrameType instance. */ public static FrameType single() { return FrameType.fetchType(null, 0); } /** * Constructs an object-based single FrameType of the specified class. * @param clazz The class of the FrameType. * @return A single object-base FrameType instance of the specified class. */ public static FrameType single(Class clazz) { return FrameType.fetchType(clazz, 0); } /** * Constructs an object-based array FrameType that matches object-based FrameTypes of any class. * @return An array object-based FrameType instance. */ public static FrameType array() { return FrameType.fetchType(null, 1); } /** * Constructs an object-based array FrameType with elements of the specified class. * @param clazz The class of the array elements (not the array type). * @return An array object-based FrameType instance of the specified class. */ public static FrameType array(Class clazz) { return FrameType.fetchType(clazz, 1); } /** * Constructs a one-dimensional buffer type of the specified element. * @param elementType One of the {@code ELEMENT} constants. * @return A 1D buffer FrameType instance. */ public static FrameType buffer1D(int elementType) { return FrameType.fetchType(elementType, 1, ACCESS_UNKNOWN); } /** * Constructs a two-dimensional buffer type of the specified element. * @param elementType One of the {@code ELEMENT} constants. * @return A 2D buffer FrameType instance. */ public static FrameType buffer2D(int elementType) { return FrameType.fetchType(elementType, 2, ACCESS_UNKNOWN); } /** * Constructs a two-dimensional image type of the specified element. * @param elementType One of the {@code ELEMENT} constants. * @param accessHint A bit-mask of access flags (see {@code READ} and {@code WRITE} constants). * @return A 2D image FrameType instance. */ public static FrameType image2D(int elementType, int accessHint) { return FrameType.fetchType(elementType, 2, accessHint); } /** * Converts the current array type to a single type. * The type must be an object-based type. If the type is already a single type, this does * nothing. * @return type as a single type. */ public FrameType asSingle() { if (mElementId != ELEMENT_OBJECT) { throw new RuntimeException("Calling asSingle() on non-object type!"); } return FrameType.fetchType(mClass, 0); } /** * Converts the current single type to an array type. * The type must be an object-based type. If the type is already an array type, this does * nothing. * @return type as an array type. */ public FrameType asArray() { if (mElementId != ELEMENT_OBJECT) { throw new RuntimeException("Calling asArray() on non-object type!"); } return FrameType.fetchType(mClass, 1); } /** * Returns the FrameType's class specifier, or null if no class was set or the receiver is not * an object-based type. * @return The FrameType's class specifier or null. */ public Class getContentClass() { return mClass; } /** * Returns the FrameType's element id. * @return The element id constant. */ public int getElementId() { return mElementId; } /** * Returns the number of bytes of the FrameType's element, or 0 if no such size can be * determined. * @return The number of bytes of the FrameType's element. */ public int getElementSize() { switch (mElementId) { case ELEMENT_INT8: return 1; case ELEMENT_INT16: return 2; case ELEMENT_INT32: case ELEMENT_FLOAT32: case ELEMENT_RGBA8888: return 4; case ELEMENT_INT64: case ELEMENT_FLOAT64: return 4; default: return 0; } } /** * Returns the access hints bit-mask of the FrameType. * @return The access hints bit-mask of the FrameType. */ public int getAccessHints() { return mAccessHints; } /** * Returns the number of dimensions of the FrameType or -1 if no dimensions were set. * @return The number of dimensions of the FrameType. */ public int getNumberOfDimensions() { return mDimensions; } /** * Returns true, if the FrameType is fully specified. * * A FrameType is fully specified if its element and dimensions are specified. * * @return true, if the FrameType is fully specified. */ public boolean isSpecified() { return mElementId != ELEMENT_DONTCARE && mDimensions >= 0; } @Override public boolean equals(Object object) { if (object instanceof FrameType) { FrameType type = (FrameType) object; return mElementId == type.mElementId && mDimensions == type.mDimensions && mAccessHints == type.mAccessHints && mClass == type.mClass; } return false; } @Override public int hashCode() { return mElementId ^ mDimensions ^ mAccessHints ^ mClass.hashCode(); } @Override public String toString() { String result = elementToString(mElementId, mClass) + "[" + mDimensions + "]"; if ((mAccessHints & READ_CPU) != 0) { result += "(rcpu)"; } if ((mAccessHints & READ_GPU) != 0) { result += "(rgpu)"; } if ((mAccessHints & READ_ALLOCATION) != 0) { result += "(ralloc)"; } if ((mAccessHints & WRITE_CPU) != 0) { result += "(wcpu)"; } if ((mAccessHints & WRITE_GPU) != 0) { result += "(wgpu)"; } if ((mAccessHints & WRITE_ALLOCATION) != 0) { result += "(walloc)"; } return result; } String keyString() { return keyValueForType(mElementId, mDimensions, mAccessHints, mClass); } static FrameType tryMerge(FrameType writer, FrameType reader) { if (writer.mElementId == ELEMENT_DONTCARE) { return reader; } else if (reader.mElementId == ELEMENT_DONTCARE) { return writer; } else if (writer.mElementId == ELEMENT_OBJECT && reader.mElementId == ELEMENT_OBJECT) { return tryMergeObjectTypes(writer, reader); } else if (writer.mDimensions > 0 && writer.mElementId == reader.mElementId) { return tryMergeBuffers(writer, reader); } else { return null; } } static FrameType tryMergeObjectTypes(FrameType writer, FrameType reader) { int dimensions = Math.max(writer.mDimensions, reader.mDimensions); Class mergedClass = mergeClasses(writer.mClass, reader.mClass); boolean success = mergedClass != null || writer.mClass == null; return success ? FrameType.fetchType(mergedClass, dimensions) : null; } static FrameType tryMergeBuffers(FrameType writer, FrameType reader) { if (writer.mDimensions == reader.mDimensions) { int accessHints = writer.mAccessHints | reader.mAccessHints; return FrameType.fetchType(writer.mElementId, writer.mDimensions, accessHints); } return null; } static FrameType merge(FrameType writer, FrameType reader) { FrameType result = tryMerge(writer, reader); if (result == null) { throw new RuntimeException( "Incompatible types in connection: " + writer + " vs. " + reader + "!"); } return result; } private static String keyValueForType(int elemId, int dims, int hints, Class clazz) { return elemId + ":" + dims + ":" + hints + ":" + (clazz != null ? clazz.getName() : "0"); } private static String elementToString(int elemId, Class clazz) { switch (elemId) { case ELEMENT_INT8: return "int8"; case ELEMENT_INT16: return "int16"; case ELEMENT_INT32: return "int32"; case ELEMENT_INT64: return "int64"; case ELEMENT_FLOAT32: return "float32"; case ELEMENT_FLOAT64: return "float64"; case ELEMENT_RGBA8888: return "rgba8888"; case ELEMENT_OBJECT: return "<" + (clazz == null ? "*" : clazz.getSimpleName()) + ">"; case ELEMENT_DONTCARE: return "*"; default: return "?"; } } private static Class mergeClasses(Class classA, Class classB) { // Return the most specialized class. if (classA == null) { return classB; } else if (classB == null) { return classA; } else if (classA.isAssignableFrom(classB)) { return classB; } else if (classB.isAssignableFrom(classA)) { return classA; } else { return null; } } private static FrameType fetchType(int elementId, int dimensions, int accessHints) { return fetchType(elementId, dimensions, accessHints, null); } private static FrameType fetchType(Class clazz, int dimensions) { return fetchType(ELEMENT_OBJECT, dimensions, ACCESS_UNKNOWN, clazz); } private static FrameType fetchType( int elementId, int dimensions, int accessHints, Class clazz) { String typeKey = FrameType.keyValueForType(elementId, dimensions, accessHints, clazz); FrameType type = mTypeCache.get(typeKey); if (type == null) { type = new FrameType(elementId, dimensions, accessHints, clazz); mTypeCache.put(typeKey, type); } return type; } private FrameType(int elementId, int dimensions, int accessHints, Class clazz) { mElementId = elementId; mDimensions = dimensions; mClass = clazz; mAccessHints = accessHints; } }