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 android.os.SystemClock;
     20 
     21 import java.util.ArrayList;
     22 import java.util.HashMap;
     23 import java.util.Map;
     24 import java.util.concurrent.atomic.AtomicBoolean;
     25 
     26 /**
     27  * Filters are the processing nodes of the filter graphs.
     28  *
     29  * Filters may have any number of input and output ports, through which the data frames flow.
     30  * TODO: More documentation on filter life-cycle, port and type checking, GL and RenderScript, ...
     31  */
     32 public abstract class Filter {
     33 
     34     private static class State {
     35         private static final int STATE_UNPREPARED = 1;
     36         private static final int STATE_PREPARED = 2;
     37         private static final int STATE_OPEN = 3;
     38         private static final int STATE_CLOSED = 4;
     39         private static final int STATE_DESTROYED = 5;
     40 
     41         public int current = STATE_UNPREPARED;
     42 
     43         public synchronized boolean check(int state) {
     44             return current == state;
     45         }
     46 
     47     }
     48 
     49     private final int REQUEST_FLAG_NONE = 0;
     50     private final int REQUEST_FLAG_CLOSE = 1;
     51 
     52     private String mName;
     53     private MffContext mContext;
     54     private FilterGraph mFilterGraph;
     55 
     56     private State mState = new State();
     57     private int mRequests = REQUEST_FLAG_NONE;
     58 
     59     private int mMinimumAvailableInputs = 1;
     60     private int mMinimumAvailableOutputs = 1;
     61 
     62     private int mScheduleCount = 0;
     63     private long mLastScheduleTime = 0;
     64 
     65     private boolean mIsActive = true;
     66     private AtomicBoolean mIsSleeping = new AtomicBoolean(false);
     67 
     68     private long mCurrentTimestamp = Frame.TIMESTAMP_NOT_SET;
     69 
     70     private HashMap<String, InputPort> mConnectedInputPorts = new HashMap<String, InputPort>();
     71     private HashMap<String, OutputPort> mConnectedOutputPorts = new HashMap<String, OutputPort>();
     72 
     73     private InputPort[] mConnectedInputPortArray = null;
     74     private OutputPort[] mConnectedOutputPortArray = null;
     75 
     76     private ArrayList<Frame> mAutoReleaseFrames = new ArrayList<Frame>();
     77 
     78 
     79     /**
     80      * Constructs a new filter.
     81      * A filter is bound to a specific MffContext. Its name can be any String value, but it must
     82      * be unique within the filter graph.
     83      *
     84      * Note that names starting with "$" are reserved for internal use, and should not be used.
     85      *
     86      * @param context The MffContext in which the filter will live.
     87      * @param name The name of the filter.
     88      */
     89     protected Filter(MffContext context, String name) {
     90         mName = name;
     91         mContext = context;
     92     }
     93 
     94     /**
     95      * Checks whether the filter class is available on this platform.
     96      * Some filters may not be installed on all platforms and can therefore not be instantiated.
     97      * Before instantiating a filter, check if it is available by using this method.
     98      *
     99      * This method uses the shared FilterFactory to check whether the filter class is available.
    100      *
    101      * @param filterClassName The fully qualified class name of the Filter class.
    102      * @return true, if filters of the specified class name are available.
    103      */
    104     public static final boolean isAvailable(String filterClassName) {
    105         return FilterFactory.sharedFactory().isFilterAvailable(filterClassName);
    106     }
    107 
    108     /**
    109      * Returns the name of this filter.
    110      *
    111      * @return the name of the filter (specified during construction).
    112      */
    113     public String getName() {
    114         return mName;
    115     }
    116 
    117     /**
    118      * Returns the signature of this filter.
    119      *
    120      * Subclasses should override this and return their filter signature. The default
    121      * implementation returns a generic signature with no constraints.
    122      *
    123      * This method may be called at any time.
    124      *
    125      * @return the Signature instance for this filter.
    126      */
    127     public Signature getSignature() {
    128         return new Signature();
    129     }
    130 
    131     /**
    132      * Returns the MffContext that the filter resides in.
    133      *
    134      * @return the MffContext of the filter.
    135      */
    136     public MffContext getContext() {
    137         return mContext;
    138     }
    139 
    140     /**
    141      * Returns true, if the filter is active.
    142      * TODO: thread safety?
    143      *
    144      * @return true, if the filter is active.
    145      */
    146     public boolean isActive() {
    147         return mIsActive;
    148     }
    149 
    150     /**
    151      * Activates the current filter.
    152      * Only active filters can be scheduled for execution. This method can only be called if the
    153      * GraphRunner that is executing the filter is stopped or paused.
    154      */
    155     public void activate() {
    156         assertIsPaused();
    157         if (!mIsActive) {
    158             mIsActive = true;
    159         }
    160     }
    161 
    162     /**
    163      * Deactivates the current filter.
    164      * Only active filters can be scheduled for execution. This method can only be called if the
    165      * GraphRunner that is executing the filter is stopped or paused.
    166      */
    167     public void deactivate() {
    168         // TODO: Support close-on-deactivate (must happen in processing thread).
    169         assertIsPaused();
    170         if (mIsActive) {
    171             mIsActive = false;
    172         }
    173     }
    174 
    175     /**
    176      * Returns the filter's set of input ports.
    177      * Note that this contains only the *connected* input ports. To retrieve all
    178      * input ports that this filter accepts, one has to go via the filter's Signature.
    179      *
    180      * @return An array containing all connected input ports.
    181      */
    182     public final InputPort[] getConnectedInputPorts() {
    183         return mConnectedInputPortArray;
    184     }
    185 
    186     /**
    187      * Returns the filter's set of output ports.
    188      * Note that this contains only the *connected* output ports. To retrieve all
    189      * output ports that this filter provides, one has to go via the filter's Signature.
    190      *
    191      * @return An array containing all connected output ports.
    192      */
    193     public final OutputPort[] getConnectedOutputPorts() {
    194         return mConnectedOutputPortArray;
    195     }
    196 
    197     /**
    198      * Returns the input port with the given name.
    199      * Note that this can only access the *connected* input ports. To retrieve all
    200      * input ports that this filter accepts, one has to go via the filter's Signature.
    201      *
    202      * @return the input port with the specified name, or null if no connected input port
    203      *  with this name exists.
    204      */
    205     public final InputPort getConnectedInputPort(String name) {
    206         return mConnectedInputPorts.get(name);
    207     }
    208 
    209     /**
    210      * Returns the output port with the given name.
    211      * Note that this can only access the *connected* output ports. To retrieve all
    212      * output ports that this filter provides, one has to go via the filter's Signature.
    213      *
    214      * @return the output port with the specified name, or null if no connected output port
    215      *  with this name exists.
    216      */
    217     public final OutputPort getConnectedOutputPort(String name) {
    218         return mConnectedOutputPorts.get(name);
    219     }
    220 
    221     /**
    222      * Called when an input port has been attached in the graph.
    223      * Override this method, in case you want to be informed of any connected input ports, or make
    224      * modifications to them. Note that you may not assume that any other ports have been attached
    225      * already. If you have dependencies on other ports, override
    226      * {@link #onInputPortOpen(InputPort)}. The default implementation does nothing.
    227      *
    228      * @param port The InputPort instance that was attached.
    229      */
    230     protected void onInputPortAttached(InputPort port) {
    231     }
    232 
    233     /**
    234      * Called when an output port has been attached in the graph.
    235      * Override this method, in case you want to be informed of any connected output ports, or make
    236      * modifications to them. Note that you may not assume that any other ports have been attached
    237      * already. If you have dependencies on other ports, override
    238      * {@link #onOutputPortOpen(OutputPort)}. The default implementation does nothing.
    239      *
    240      * @param port The OutputPort instance that was attached.
    241      */
    242     protected void onOutputPortAttached(OutputPort port) {
    243     }
    244 
    245     /**
    246      * Called when an input port is opened on this filter.
    247      * Input ports are opened by the data produce, that is the filter that is connected to an
    248      * input port. Override this if you need to make modifications to the port before processing
    249      * begins. Note, that this is only called if the connected filter is scheduled. You may assume
    250      * that all ports are attached when this is called.
    251      *
    252      * @param port The InputPort instance that was opened.
    253      */
    254     protected void onInputPortOpen(InputPort port) {
    255     }
    256 
    257     /**
    258      * Called when an output port is opened on this filter.
    259      * Output ports are opened when the filter they are attached to is opened. Override this if you
    260      * need to make modifications to the port before processing begins. Note, that this is only
    261      * called if the filter is scheduled. You may assume that all ports are attached when this is
    262      * called.
    263      *
    264      * @param port The OutputPort instance that was opened.
    265      */
    266     protected void onOutputPortOpen(OutputPort port) {
    267     }
    268 
    269     /**
    270      * Returns true, if the filter is currently open.
    271      * @return true, if the filter is currently open.
    272      */
    273     public final boolean isOpen() {
    274         return mState.check(State.STATE_OPEN);
    275     }
    276 
    277     @Override
    278     public String toString() {
    279         return mName + " (" + getClass().getSimpleName() + ")";
    280     }
    281 
    282     /**
    283      * Called when filter is prepared.
    284      * Subclasses can override this to prepare the filter for processing. This method gets called
    285      * once only just before the filter is scheduled for processing the first time.
    286      *
    287      * @see #onTearDown()
    288      */
    289     protected void onPrepare() {
    290     }
    291 
    292     /**
    293      * Called when the filter is opened.
    294      * Subclasses can override this to perform any kind of initialization just before processing
    295      * starts. This method may be called any number of times, but is always balanced with an
    296      * {@link #onClose()} call.
    297      *
    298      * @see #onClose()
    299      */
    300     protected void onOpen() {
    301     }
    302 
    303     /**
    304      * Called to perform processing on Frame data.
    305      * This is the only method subclasses must override. It is called every time the filter is
    306      * ready for processing. Typically this is when there is input data to process and available
    307      * output ports, but may differ depending on the port configuration.
    308      */
    309     protected abstract void onProcess();
    310 
    311     /**
    312      * Called when the filter is closed.
    313      * Subclasses can override this to perform any kind of post-processing steps. Processing will
    314      * not resume until {@link #onOpen()} is called again. This method is only called if the filter
    315      * is open.
    316      *
    317      * @see #onOpen()
    318      */
    319     protected void onClose() {
    320     }
    321 
    322     /**
    323      * Called when the filter is torn down.
    324      * Subclasses can override this to perform clean-up tasks just before the filter is disposed of.
    325      * It is called when the filter graph that the filter belongs to is disposed.
    326      *
    327      * @see #onPrepare()
    328      */
    329     protected void onTearDown() {
    330     }
    331 
    332     /**
    333      * Check if the input conditions are met in order to schedule this filter.
    334      *
    335      * This is used by {@link #canSchedule()} to determine if the input-port conditions given by
    336      * the filter are met. Subclasses that override scheduling behavior can make use of this
    337      * function.
    338      *
    339      * @return true, if the filter's input conditions are met.
    340      */
    341     protected boolean inputConditionsMet() {
    342         if (mConnectedInputPortArray.length > 0) {
    343             int inputFrames = 0;
    344             // [Non-iterator looping]
    345             for (int i = 0; i < mConnectedInputPortArray.length; ++i) {
    346                 if (!mConnectedInputPortArray[i].conditionsMet()) {
    347                     return false;
    348                 } else if (mConnectedInputPortArray[i].hasFrame()) {
    349                     ++inputFrames;
    350                 }
    351             }
    352             if (inputFrames < mMinimumAvailableInputs) {
    353                 return false;
    354             }
    355         }
    356         return true;
    357     }
    358 
    359     /**
    360      * Check if the output conditions are met in order to schedule this filter.
    361      *
    362      * This is used by {@link #canSchedule()} to determine if the output-port conditions given by
    363      * the filter are met. Subclasses that override scheduling behavior can make use of this
    364      * function.
    365      *
    366      * @return true, if the filter's output conditions are met.
    367      */
    368     protected boolean outputConditionsMet() {
    369         if (mConnectedOutputPortArray.length > 0) {
    370             int availableOutputs = 0;
    371             for (int i = 0; i < mConnectedOutputPortArray.length; ++i) {
    372                 if (!mConnectedOutputPortArray[i].conditionsMet()) {
    373                     return false;
    374                 } else if (mConnectedOutputPortArray[i].isAvailable()) {
    375                     ++availableOutputs;
    376                 }
    377             }
    378             if (availableOutputs < mMinimumAvailableOutputs) {
    379                 return false;
    380             }
    381         }
    382         return true;
    383     }
    384 
    385     /**
    386      * Check if the Filter is in a state so that it can be scheduled.
    387      *
    388      * When overriding the filter's {@link #canSchedule()} method, you should never allow
    389      * scheduling a filter that is not in a schedulable state. This will result in undefined
    390      * behavior.
    391      *
    392      * @return true, if the filter is in a schedulable state.
    393      */
    394     protected boolean inSchedulableState() {
    395         return (mIsActive && !mState.check(State.STATE_CLOSED));
    396     }
    397 
    398     /**
    399      * Returns true if the filter can be currently scheduled.
    400      *
    401      * Filters may override this method if they depend on custom factors that determine whether
    402      * they can be scheduled or not. The scheduler calls this method to determine whether or not
    403      * a filter can be scheduled for execution. It does not guarantee that it will be executed.
    404      * It is strongly recommended to call super's implementation to make sure your filter can be
    405      * scheduled based on its state, input and output ports.
    406      *
    407      * @return true, if the filter can be scheduled.
    408      */
    409     protected boolean canSchedule() {
    410         return inSchedulableState() && inputConditionsMet() && outputConditionsMet();
    411     }
    412 
    413     /**
    414      * Returns the current FrameManager instance.
    415      * @return the current FrameManager instance or null if there is no FrameManager set up yet.
    416      */
    417     protected final FrameManager getFrameManager() {
    418         return mFilterGraph.mRunner != null ? mFilterGraph.mRunner.getFrameManager() : null;
    419     }
    420 
    421     /**
    422      * Returns whether the GraphRunner for this filter is running.
    423      *
    424      * Generally, this method should not be used for performing operations that need to be carried
    425      * out before running begins. Use {@link #performPreparation(Runnable)} for this.
    426      *
    427      * @return true, if the GraphRunner for this filter is running.
    428      */
    429     protected final boolean isRunning() {
    430         return mFilterGraph != null && mFilterGraph.mRunner != null
    431                 && mFilterGraph.mRunner.isRunning();
    432     }
    433 
    434     /**
    435      * Performs operations before the filter is running.
    436      *
    437      * Use this method when your filter requires to perform operations while the graph is not
    438      * running. The filter will not be scheduled for execution until your method has completed
    439      * execution.
    440      */
    441     protected final boolean performPreparation(Runnable runnable) {
    442         synchronized (mState) {
    443             if (mState.current == State.STATE_OPEN) {
    444                 return false;
    445             } else {
    446                 runnable.run();
    447                 return true;
    448             }
    449         }
    450     }
    451 
    452     /**
    453      * Request that this filter be closed after the current processing step.
    454      *
    455      * Implementations may call this within their {@link #onProcess()} calls to indicate that the
    456      * filter is done processing and wishes to be closed. After such a request the filter will be
    457      * closed and no longer receive {@link #onProcess()} calls.
    458      *
    459      * @see #onClose()
    460      * @see #onProcess()
    461      */
    462     protected final void requestClose() {
    463         mRequests |= REQUEST_FLAG_CLOSE;
    464     }
    465 
    466     /**
    467      * Sets the minimum number of input frames required to process.
    468      * A filter will not be scheduled unless at least a certain number of input frames are available
    469      * on the input ports. This is only relevant if the filter has input ports and is not waiting on
    470      * all ports.
    471      * The default value is 1.
    472      *
    473      * @param count the minimum number of frames required to process.
    474      * @see #getMinimumAvailableInputs()
    475      * @see #setMinimumAvailableOutputs(int)
    476      * @see InputPort#setWaitsForFrame(boolean)
    477      */
    478     protected final void setMinimumAvailableInputs(int count) {
    479         mMinimumAvailableInputs = count;
    480     }
    481 
    482     /**
    483      * Returns the minimum number of input frames required to process this filter.
    484      * The default value is 1.
    485      *
    486      * @return the minimum number of input frames required to process.
    487      * @see #setMinimumAvailableInputs(int)
    488      */
    489     protected final int getMinimumAvailableInputs() {
    490         return mMinimumAvailableInputs;
    491     }
    492 
    493     /**
    494      * Sets the minimum number of available output ports required to process.
    495      * A filter will not be scheduled unless atleast a certain number of output ports are available.
    496      * This is only relevant if the filter has output ports and is not waiting on all ports. The
    497      * default value is 1.
    498      *
    499      * @param count the minimum number of frames required to process.
    500      * @see #getMinimumAvailableOutputs()
    501      * @see #setMinimumAvailableInputs(int)
    502      * @see OutputPort#setWaitsUntilAvailable(boolean)
    503      */
    504     protected final void setMinimumAvailableOutputs(int count) {
    505         mMinimumAvailableOutputs = count;
    506     }
    507 
    508     /**
    509      * Returns the minimum number of available outputs required to process this filter.
    510      * The default value is 1.
    511      *
    512      * @return the minimum number of available outputs required to process.
    513      * @see #setMinimumAvailableOutputs(int)
    514      */
    515     protected final int getMinimumAvailableOutputs() {
    516         return mMinimumAvailableOutputs;
    517     }
    518 
    519     /**
    520      * Puts the filter to sleep so that it is no longer scheduled.
    521      * To resume scheduling the filter another thread must call wakeUp() on this filter.
    522      */
    523     protected final void enterSleepState() {
    524         mIsSleeping.set(true);
    525     }
    526 
    527     /**
    528      * Wakes the filter and resumes scheduling.
    529      * This is generally called from another thread to signal that this filter should resume
    530      * processing. Does nothing if filter is not sleeping.
    531      */
    532     protected final void wakeUp() {
    533         if (mIsSleeping.getAndSet(false)) {
    534             if (isRunning()) {
    535                 mFilterGraph.mRunner.signalWakeUp();
    536             }
    537         }
    538     }
    539 
    540     /**
    541      * Returns whether this Filter is allowed to use OpenGL.
    542      *
    543      * Filters may use OpenGL if the MffContext supports OpenGL and its GraphRunner allows it.
    544      *
    545      * @return true, if this Filter is allowed to use OpenGL.
    546      */
    547    protected final boolean isOpenGLSupported() {
    548         return mFilterGraph.mRunner.isOpenGLSupported();
    549     }
    550 
    551     /**
    552      * Connect an output port to an input port of another filter.
    553      * Connects the output port with the specified name to the input port with the specified name
    554      * of the specified filter. If the input or output ports do not exist already, they are
    555      * automatically created and added to the respective filter.
    556      */
    557     final void connect(String outputName, Filter targetFilter, String inputName) {
    558         // Make sure not connected already
    559         if (getConnectedOutputPort(outputName) != null) {
    560             throw new RuntimeException("Attempting to connect already connected output port '"
    561                 + outputName + "' of filter " + this + "'!");
    562         } else if (targetFilter.getConnectedInputPort(inputName) != null) {
    563             throw new RuntimeException("Attempting to connect already connected input port '"
    564                 + inputName + "' of filter " + targetFilter + "'!");
    565         }
    566 
    567         // Establish connection
    568         InputPort inputPort = targetFilter.newInputPort(inputName);
    569         OutputPort outputPort = newOutputPort(outputName);
    570         outputPort.setTarget(inputPort);
    571 
    572         // Fire attachment callbacks
    573         targetFilter.onInputPortAttached(inputPort);
    574         onOutputPortAttached(outputPort);
    575 
    576         // Update array of ports (which is maintained for more efficient access)
    577         updatePortArrays();
    578     }
    579 
    580     final Map<String, InputPort> getConnectedInputPortMap() {
    581         return mConnectedInputPorts;
    582     }
    583 
    584     final Map<String, OutputPort> getConnectedOutputPortMap() {
    585         return mConnectedOutputPorts;
    586     }
    587 
    588     final void execute() {
    589         synchronized (mState) {
    590             autoPullInputs();
    591             mLastScheduleTime = SystemClock.elapsedRealtime();
    592             if (mState.current == State.STATE_UNPREPARED) {
    593                 onPrepare();
    594                 mState.current = State.STATE_PREPARED;
    595             }
    596             if (mState.current == State.STATE_PREPARED) {
    597                 openPorts();
    598                 onOpen();
    599                 mState.current = State.STATE_OPEN;
    600             }
    601             if (mState.current == State.STATE_OPEN) {
    602                 onProcess();
    603                 if (mRequests != REQUEST_FLAG_NONE) {
    604                     processRequests();
    605                 }
    606             }
    607         }
    608         autoReleaseFrames();
    609         ++mScheduleCount;
    610     }
    611 
    612     final void performClose() {
    613         synchronized (mState) {
    614             if (mState.current == State.STATE_OPEN) {
    615                 onClose();
    616                 mIsSleeping.set(false);
    617                 mState.current = State.STATE_CLOSED;
    618                 mCurrentTimestamp = Frame.TIMESTAMP_NOT_SET;
    619             }
    620         }
    621     }
    622 
    623     final void softReset() {
    624         synchronized (mState) {
    625             performClose();
    626             if (mState.current == State.STATE_CLOSED) {
    627                 mState.current = State.STATE_PREPARED;
    628             }
    629         }
    630     }
    631 
    632     final void performTearDown() {
    633         synchronized (mState) {
    634             if (mState.current == State.STATE_OPEN) {
    635                 throw new RuntimeException("Attempting to tear-down filter " + this + " which is "
    636                     + "in an open state!");
    637             } else if (mState.current != State.STATE_DESTROYED
    638                     && mState.current != State.STATE_UNPREPARED) {
    639                 onTearDown();
    640                 mState.current = State.STATE_DESTROYED;
    641             }
    642         }
    643     }
    644 
    645     final void insertIntoFilterGraph(FilterGraph graph) {
    646         mFilterGraph = graph;
    647         updatePortArrays();
    648     }
    649 
    650     final int getScheduleCount() {
    651         return mScheduleCount;
    652     }
    653 
    654     final void resetScheduleCount() {
    655         mScheduleCount = 0;
    656     }
    657 
    658     final void openPorts() {
    659         // Opening the output ports will open the connected input ports
    660         for (OutputPort outputPort : mConnectedOutputPorts.values()) {
    661             openOutputPort(outputPort);
    662         }
    663     }
    664 
    665     final void addAutoReleaseFrame(Frame frame) {
    666         mAutoReleaseFrames.add(frame);
    667     }
    668 
    669     final long getCurrentTimestamp() {
    670         return mCurrentTimestamp;
    671     }
    672 
    673     final void onPulledFrameWithTimestamp(long timestamp) {
    674         if (timestamp > mCurrentTimestamp || mCurrentTimestamp == Frame.TIMESTAMP_NOT_SET) {
    675             mCurrentTimestamp = timestamp;
    676         }
    677     }
    678 
    679     final void openOutputPort(OutputPort outPort) {
    680         if (outPort.getQueue() == null) {
    681             try {
    682                 FrameQueue.Builder builder = new FrameQueue.Builder();
    683                 InputPort inPort = outPort.getTarget();
    684                 outPort.onOpen(builder);
    685                 inPort.onOpen(builder);
    686                 Filter targetFilter = inPort.getFilter();
    687                 String queueName = mName + "[" + outPort.getName() + "] -> " + targetFilter.mName
    688                         + "[" + inPort.getName() + "]";
    689                 FrameQueue queue = builder.build(queueName);
    690                 outPort.setQueue(queue);
    691                 inPort.setQueue(queue);
    692             } catch (RuntimeException e) {
    693                 throw new RuntimeException("Could not open output port " + outPort + "!", e);
    694             }
    695         }
    696     }
    697 
    698     final boolean isSleeping() {
    699         return mIsSleeping.get();
    700     }
    701 
    702     final long getLastScheduleTime() {
    703         return mLastScheduleTime ;
    704     }
    705 
    706     private final void autoPullInputs() {
    707         // [Non-iterator looping]
    708         for (int i = 0; i < mConnectedInputPortArray.length; ++i) {
    709             InputPort port = mConnectedInputPortArray[i];
    710             if (port.hasFrame() && port.isAutoPullEnabled()) {
    711                 mConnectedInputPortArray[i].pullFrame();
    712             }
    713         }
    714     }
    715 
    716     private final void autoReleaseFrames() {
    717         // [Non-iterator looping]
    718         for (int i = 0; i < mAutoReleaseFrames.size(); ++i) {
    719             mAutoReleaseFrames.get(i).release();
    720         }
    721         mAutoReleaseFrames.clear();
    722     }
    723 
    724     private final InputPort newInputPort(String name) {
    725         InputPort result = mConnectedInputPorts.get(name);
    726         if (result == null) {
    727             Signature.PortInfo info = getSignature().getInputPortInfo(name);
    728             result = new InputPort(this, name, info);
    729             mConnectedInputPorts.put(name, result);
    730         }
    731         return result;
    732     }
    733 
    734     private final OutputPort newOutputPort(String name) {
    735         OutputPort result = mConnectedOutputPorts.get(name);
    736         if (result == null) {
    737             Signature.PortInfo info = getSignature().getOutputPortInfo(name);
    738             result = new OutputPort(this, name, info);
    739             mConnectedOutputPorts.put(name, result);
    740         }
    741         return result;
    742     }
    743 
    744     private final void processRequests() {
    745         if ((mRequests & REQUEST_FLAG_CLOSE) != 0) {
    746             performClose();
    747             mRequests = REQUEST_FLAG_NONE;
    748         }
    749     }
    750 
    751     private void assertIsPaused() {
    752         GraphRunner runner = GraphRunner.current();
    753         if (runner != null && !runner.isPaused() && !runner.isStopped()) {
    754             throw new RuntimeException("Attempting to modify filter state while runner is "
    755                 + "executing. Please pause or stop the runner first!");
    756         }
    757     }
    758 
    759     private final void updatePortArrays() {
    760         // Copy our port-maps to arrays for faster non-iterator access
    761         mConnectedInputPortArray = mConnectedInputPorts.values().toArray(new InputPort[0]);
    762         mConnectedOutputPortArray = mConnectedOutputPorts.values().toArray(new OutputPort[0]);
    763     }
    764 
    765 }
    766 
    767