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");
      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 package androidx.media.filterfw;
     18 
     19 import java.lang.reflect.Field;
     20 
     21 /**
     22  * Input ports are the receiving ports of frames in a filter.
     23  * <p>
     24  * InputPort instances receive Frame data from connected OutputPort instances of a previous filter.
     25  * Frames flow from output ports to input ports. Filters can process frame data by calling
     26  * {@link #pullFrame()} on an input port. If the input port is set to wait for an input frame
     27  * (see {@link #setWaitsForFrame(boolean)}), there is guaranteed to be Frame on the port before
     28  * {@code onProcess()} is called. This is the default setting. Otherwise, calling
     29  * {@link #pullFrame()} may return a value of {@code null}.
     30  * <p/><p>
     31  * InputPorts may be bound to fields of the Filter. When an input port is bound to a field, Frame
     32  * values will be assigned to the field once a Frame is received on that port. The Frame value must
     33  * be of a type that is compatible with the field type.
     34  * </p>
     35  */
     36 public final class InputPort {
     37 
     38     private Filter mFilter;
     39     private String mName;
     40     private Signature.PortInfo mInfo;
     41     private FrameListener mListener = null;
     42     private FrameQueue.Builder mQueueBuilder = null;
     43     private FrameQueue mQueue = null;
     44     private boolean mWaitForFrame = true;
     45     private boolean mAutoPullEnabled = false;
     46 
     47     public interface FrameListener {
     48         public void onFrameReceived(InputPort port, Frame frame);
     49     }
     50 
     51     private class FieldBinding implements FrameListener {
     52         private Field mField;
     53 
     54         public FieldBinding(Field field) {
     55             mField = field;
     56         }
     57 
     58         @Override
     59         public void onFrameReceived(InputPort port, Frame frame) {
     60             try {
     61                 if(port.mInfo.type.getNumberOfDimensions() > 0) {
     62                     FrameValues frameValues = frame.asFrameValues();
     63                     mField.set(mFilter, frameValues.getValues());
     64                 } else {
     65                     FrameValue frameValue = frame.asFrameValue();
     66                     mField.set(mFilter, frameValue.getValue());
     67                 }
     68             } catch (Exception e) {
     69                 throw new RuntimeException("Assigning frame " + frame + " to field "
     70                     + mField + " of filter " + mFilter + " caused exception!", e);
     71             }
     72         }
     73     }
     74 
     75     /**
     76      * Attach this input port to an output port for frame passing.
     77      *
     78      * Use this method whenever you plan on passing a Frame through from an input port to an
     79      * output port. This must be called from inside
     80      * {@link Filter#onInputPortAttached(InputPort) onInputPortAttached}.
     81      *
     82      * @param outputPort the output port that Frames will be pushed to.
     83      */
     84     public void attachToOutputPort(OutputPort outputPort) {
     85         assertInAttachmentStage();
     86         mFilter.openOutputPort(outputPort);
     87         mQueueBuilder.attachQueue(outputPort.getQueue());
     88     }
     89 
     90     /**
     91      * Bind this input port to the specified listener.
     92      *
     93      * Use this when you wish to be notified of incoming frames. The listener method
     94      * {@link FrameListener#onFrameReceived(InputPort, Frame)} will be called once a Frame is pulled
     95      * on this port. Typically this is called from inside
     96      * {@link Filter#onInputPortAttached(InputPort) onInputPortAttached}, and used in
     97      * conjunction with {@link #setAutoPullEnabled(boolean)}. Overrides any previous bindings.
     98      *
     99      * @param listener the listener to handle incoming Frames.
    100      */
    101     public void bindToListener(FrameListener listener) {
    102         assertInAttachmentStage();
    103         mListener = listener;
    104     }
    105 
    106     /**
    107      * Bind this input port to the specified field.
    108      *
    109      * Use this when you wish to pull frames directly into a field of the filter. This requires
    110      * that the input frames can be interpreted as object-based frames of the field's class.
    111      * Overrides any previous bindings.
    112      *
    113      * This is typically called from inside
    114      * {@link Filter#onInputPortAttached(InputPort) onInputPortAttached}, and used in
    115      * conjunction with {@link #setAutoPullEnabled(boolean)}.
    116      *
    117      * @param field the field to pull frame data into.
    118      * @see #bindToFieldNamed(String)
    119      * @see #setAutoPullEnabled(boolean)
    120      */
    121     public void bindToField(Field field) {
    122         assertInAttachmentStage();
    123         mListener = new FieldBinding(field);
    124     }
    125 
    126     /**
    127      * Bind this input port to the field with the specified name.
    128      *
    129      * Use this when you wish to pull frames directly into a field of the filter. This requires
    130      * that the input frames can be interpreted as object-based frames of the field's class.
    131      * Overrides any previous bindings.
    132      *
    133      * This is typically called from inside
    134      * {@link Filter#onInputPortAttached(InputPort) onInputPortAttached}, and used in
    135      * conjunction with {@link #setAutoPullEnabled(boolean)}.
    136      *
    137      * @param fieldName the field to pull frame data into.
    138      * @see #bindToField(Field)
    139      * @see #setAutoPullEnabled(boolean)
    140      */
    141     public void bindToFieldNamed(String fieldName) {
    142         Field field = findFieldNamed(fieldName, mFilter.getClass());
    143         if (field == null) {
    144             throw new IllegalArgumentException("Attempting to bind to unknown field '"
    145                 + fieldName + "'!");
    146         }
    147         bindToField(field);
    148     }
    149 
    150     /**
    151      * Set whether the InputPort automatically pulls frames.
    152      * This is typically only used when the port is bound to another target.
    153      * @param enabled true, if frames should be automatically pulled on this port.
    154      */
    155     public void setAutoPullEnabled(boolean enabled) {
    156         mAutoPullEnabled = enabled;
    157     }
    158 
    159     /**
    160      * Returns whether the InputPort automatically pulls frames.
    161      * @return true, if frames are automatically pulled on this port.
    162      */
    163     public boolean isAutoPullEnabled() {
    164         return mAutoPullEnabled;
    165     }
    166 
    167     /**
    168      * Pull a waiting a frame from the port.
    169      *
    170      * Call this to pull a frame from the input port for processing. If no frame is waiting on the
    171      * input port, returns null. After this call the port will have no Frame waiting (empty port).
    172      * Note, that this returns a frame owned by the input queue. You must detach the frame if you
    173      * wish to hold on to it.
    174      *
    175      * @return Frame instance, or null if no frame is available for pulling.
    176      */
    177     public synchronized Frame pullFrame() {
    178         if (mQueue == null) {
    179             throw new IllegalStateException("Cannot pull frame from closed input port!");
    180         }
    181         Frame frame = mQueue.pullFrame();
    182         if (frame != null) {
    183             if (mListener != null) {
    184                 mListener.onFrameReceived(this, frame);
    185             }
    186             //Log.i("InputPort", "Adding frame " + frame + " to auto-release pool");
    187             mFilter.addAutoReleaseFrame(frame);
    188             long timestamp = frame.getTimestamp();
    189             if (timestamp != Frame.TIMESTAMP_NOT_SET) {
    190                 mFilter.onPulledFrameWithTimestamp(frame.getTimestamp());
    191             }
    192         }
    193         return frame;
    194     }
    195 
    196     public synchronized Frame peek() {
    197         if (mQueue == null) {
    198             throw new IllegalStateException("Cannot pull frame from closed input port!");
    199         }
    200         return mQueue.peek();
    201     }
    202 
    203     /**
    204      * Returns true, if the port is connected.
    205      * @return true, if there is an output port that connects to this port.
    206      */
    207     public boolean isConnected() {
    208         return mQueue != null;
    209     }
    210 
    211     /**
    212      * Returns true, if there is a frame waiting on this port.
    213      * @return true, if there is a frame waiting on this port.
    214      */
    215     public synchronized boolean hasFrame() {
    216         return mQueue != null && mQueue.canPull();
    217     }
    218 
    219     /**
    220      * Sets whether to wait for a frame on this port before processing.
    221      * When set to true, the Filter will not be scheduled for processing unless there is a Frame
    222      * waiting on this port. The default value is true.
    223      *
    224      * @param wait true, if the Filter should wait for a Frame before processing.
    225      * @see #waitsForFrame()
    226      */
    227     public void setWaitsForFrame(boolean wait) {
    228         mWaitForFrame = wait;
    229     }
    230 
    231     /**
    232      * Returns whether the filter waits for a frame on this port before processing.
    233      * @return true, if the filter waits for a frame on this port before processing.
    234      * @see #setWaitsForFrame(boolean)
    235      */
    236     public boolean waitsForFrame() {
    237         return mWaitForFrame;
    238     }
    239 
    240     /**
    241      * Returns the input port's name.
    242      * This is the name that was specified when the input port was connected.
    243      *
    244      * @return the input port's name.
    245      */
    246     public String getName() {
    247         return mName;
    248     }
    249 
    250     /**
    251      * Returns the FrameType of this port.
    252      * This is the type that was specified when the input port was declared.
    253      *
    254      * @return the input port's FrameType.
    255      */
    256     public FrameType getType() {
    257         return getQueue().getType();
    258     }
    259 
    260     /**
    261      * Return the filter object that this port belongs to.
    262      *
    263      * @return the input port's filter.
    264      */
    265     public Filter getFilter() {
    266         return mFilter;
    267     }
    268 
    269     @Override
    270     public String toString() {
    271         return mFilter.getName() + ":" + mName;
    272     }
    273 
    274     // Internal only ///////////////////////////////////////////////////////////////////////////////
    275     InputPort(Filter filter, String name, Signature.PortInfo info) {
    276         mFilter = filter;
    277         mName = name;
    278         mInfo = info;
    279     }
    280 
    281     boolean conditionsMet() {
    282         return !mWaitForFrame || hasFrame();
    283     }
    284 
    285     void onOpen(FrameQueue.Builder builder) {
    286         mQueueBuilder = builder;
    287         mQueueBuilder.setReadType(mInfo.type);
    288         mFilter.onInputPortOpen(this);
    289     }
    290 
    291     void setQueue(FrameQueue queue) {
    292         mQueue = queue;
    293         mQueueBuilder = null;
    294     }
    295 
    296     FrameQueue getQueue() {
    297         return mQueue;
    298     }
    299 
    300     void clear() {
    301         if (mQueue != null) {
    302             mQueue.clear();
    303         }
    304     }
    305 
    306     private void assertInAttachmentStage() {
    307         if (mQueueBuilder == null) {
    308             throw new IllegalStateException("Attempting to attach port while not in attachment "
    309                 + "stage!");
    310         }
    311     }
    312 
    313     private Field findFieldNamed(String fieldName, Class<?> clazz) {
    314         Field field = null;
    315         try {
    316             field = clazz.getDeclaredField(fieldName);
    317             field.setAccessible(true);
    318         } catch (NoSuchFieldException e) {
    319             Class<?> superClass = clazz.getSuperclass();
    320             if (superClass != null) {
    321                 field = findFieldNamed(fieldName, superClass);
    322             }
    323         }
    324         return field;
    325     }
    326 }
    327 
    328