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      */
any()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      */
single()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      */
single(Class<?> clazz)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      */
array()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      */
array(Class<?> clazz)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      */
buffer1D(int elementType)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      */
buffer2D(int elementType)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      */
image2D(int elementType, int accessHint)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      */
asSingle()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      */
asArray()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      */
getContentClass()217     public Class<?> getContentClass() {
218         return mClass;
219     }
220 
221     /**
222      * Returns the FrameType's element id.
223      * @return The element id constant.
224      */
getElementId()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      */
getElementSize()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      */
getAccessHints()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      */
getNumberOfDimensions()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      */
isSpecified()275     public boolean isSpecified() {
276         return mElementId != ELEMENT_DONTCARE && mDimensions >= 0;
277     }
278 
279     @Override
equals(Object object)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
hashCode()290     public int hashCode() {
291         return mElementId ^ mDimensions ^ mAccessHints ^ mClass.hashCode();
292     }
293 
294     @Override
toString()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 
keyString()318     String keyString() {
319         return keyValueForType(mElementId, mDimensions, mAccessHints, mClass);
320     }
321 
tryMerge(FrameType writer, FrameType reader)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 
tryMergeObjectTypes(FrameType writer, FrameType reader)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 
tryMergeBuffers(FrameType writer, FrameType reader)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 
merge(FrameType writer, FrameType reader)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 
keyValueForType(int elemId, int dims, int hints, Class<?> clazz)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 
elementToString(int elemId, Class<?> clazz)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 
mergeClasses(Class<?> classA, Class<?> classB)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 
fetchType(int elementId, int dimensions, int accessHints)404     private static FrameType fetchType(int elementId, int dimensions, int accessHints) {
405         return fetchType(elementId, dimensions, accessHints, null);
406     }
407 
fetchType(Class<?> clazz, int dimensions)408     private static FrameType fetchType(Class<?> clazz, int dimensions) {
409         return fetchType(ELEMENT_OBJECT, dimensions, ACCESS_UNKNOWN, clazz);
410     }
411 
fetchType( int elementId, int dimensions, int accessHints, Class<?> clazz)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 
FrameType(int elementId, int dimensions, int accessHints, Class<?> clazz)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