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 
check(int state)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      */
Filter(MffContext context, String name)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      */
isAvailable(String filterClassName)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      */
getName()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      */
getSignature()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      */
getContext()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      */
isActive()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      */
activate()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      */
deactivate()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      */
getConnectedInputPorts()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      */
getConnectedOutputPorts()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      */
getConnectedInputPort(String name)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      */
getConnectedOutputPort(String name)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      */
onInputPortAttached(InputPort port)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      */
onOutputPortAttached(OutputPort port)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      */
onInputPortOpen(InputPort port)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      */
onOutputPortOpen(OutputPort port)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      */
isOpen()273     public final boolean isOpen() {
274         return mState.check(State.STATE_OPEN);
275     }
276 
277     @Override
toString()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      */
onPrepare()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      */
onOpen()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      */
onProcess()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      */
onClose()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      */
onTearDown()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      */
inputConditionsMet()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      */
outputConditionsMet()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      */
inSchedulableState()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      */
canSchedule()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      */
getFrameManager()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      */
isRunning()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      */
performPreparation(Runnable runnable)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      */
requestClose()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      */
setMinimumAvailableInputs(int count)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      */
getMinimumAvailableInputs()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      */
setMinimumAvailableOutputs(int count)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      */
getMinimumAvailableOutputs()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      */
enterSleepState()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      */
wakeUp()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      */
isOpenGLSupported()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      */
connect(String outputName, Filter targetFilter, String inputName)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 
getConnectedInputPortMap()580     final Map<String, InputPort> getConnectedInputPortMap() {
581         return mConnectedInputPorts;
582     }
583 
getConnectedOutputPortMap()584     final Map<String, OutputPort> getConnectedOutputPortMap() {
585         return mConnectedOutputPorts;
586     }
587 
execute()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 
performClose()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 
softReset()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 
performTearDown()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 
insertIntoFilterGraph(FilterGraph graph)645     final void insertIntoFilterGraph(FilterGraph graph) {
646         mFilterGraph = graph;
647         updatePortArrays();
648     }
649 
getScheduleCount()650     final int getScheduleCount() {
651         return mScheduleCount;
652     }
653 
resetScheduleCount()654     final void resetScheduleCount() {
655         mScheduleCount = 0;
656     }
657 
openPorts()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 
addAutoReleaseFrame(Frame frame)665     final void addAutoReleaseFrame(Frame frame) {
666         mAutoReleaseFrames.add(frame);
667     }
668 
getCurrentTimestamp()669     final long getCurrentTimestamp() {
670         return mCurrentTimestamp;
671     }
672 
onPulledFrameWithTimestamp(long timestamp)673     final void onPulledFrameWithTimestamp(long timestamp) {
674         if (timestamp > mCurrentTimestamp || mCurrentTimestamp == Frame.TIMESTAMP_NOT_SET) {
675             mCurrentTimestamp = timestamp;
676         }
677     }
678 
openOutputPort(OutputPort outPort)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 
isSleeping()698     final boolean isSleeping() {
699         return mIsSleeping.get();
700     }
701 
getLastScheduleTime()702     final long getLastScheduleTime() {
703         return mLastScheduleTime ;
704     }
705 
autoPullInputs()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 
autoReleaseFrames()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 
newInputPort(String name)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 
newOutputPort(String name)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 
processRequests()744     private final void processRequests() {
745         if ((mRequests & REQUEST_FLAG_CLOSE) != 0) {
746             performClose();
747             mRequests = REQUEST_FLAG_NONE;
748         }
749     }
750 
assertIsPaused()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 
updatePortArrays()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