Home | History | Annotate | Download | only in filterfw
      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