165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn/*
265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Copyright (C) 2011 The Android Open Source Project
365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Licensed under the Apache License, Version 2.0 (the "License");
565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * you may not use this file except in compliance with the License.
665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * You may obtain a copy of the License at
765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *      http://www.apache.org/licenses/LICENSE-2.0
965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
1065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Unless required by applicable law or agreed to in writing, software
1165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * distributed under the License is distributed on an "AS IS" BASIS,
1265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * See the License for the specific language governing permissions and
1465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * limitations under the License.
1565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn */
1665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
1765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
1865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennpackage android.filterfw.core;
1965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
2065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.HashMap;
2165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.HashSet;
2265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.Iterator;
2365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.LinkedList;
2465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.Map.Entry;
2565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.Set;
2665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.Stack;
2765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
2865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.FilterContext;
2965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.KeyValueMap;
3065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterpacks.base.FrameBranch;
3165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterpacks.base.NullFilter;
3265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
3365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.util.Log;
3465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
3565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn/**
3665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * @hide
3765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn */
3865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennpublic class FilterGraph {
3965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private HashSet<Filter> mFilters = new HashSet<Filter>();
4165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private HashMap<String, Filter> mNameMap = new HashMap<String, Filter>();
4265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private HashMap<OutputPort, LinkedList<InputPort>> mPreconnections = new
4365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            HashMap<OutputPort, LinkedList<InputPort>>();
4465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public static final int AUTOBRANCH_OFF      = 0;
4665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public static final int AUTOBRANCH_SYNCED   = 1;
4765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public static final int AUTOBRANCH_UNSYNCED = 2;
4865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public static final int TYPECHECK_OFF       = 0;
5065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public static final int TYPECHECK_DYNAMIC   = 1;
5165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public static final int TYPECHECK_STRICT    = 2;
5265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mIsReady = false;
5465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int mAutoBranchMode = AUTOBRANCH_OFF;
5565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int mTypeCheckMode = TYPECHECK_STRICT;
5665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mDiscardUnconnectedOutputs = false;
5765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mLogVerbose;
5965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private String TAG = "FilterGraph";
6065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
6165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public FilterGraph() {
6265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
6365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
6465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
6565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public boolean addFilter(Filter filter) {
6665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (!containsFilter(filter)) {
6765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mFilters.add(filter);
6865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mNameMap.put(filter.getName(), filter);
6965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            return true;
7065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
7165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return false;
7265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
7365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
7465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public boolean containsFilter(Filter filter) {
7565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return mFilters.contains(filter);
7665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
7765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
7865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public Filter getFilter(String name) {
7965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return mNameMap.get(name);
8065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
8165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
8265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void connect(Filter source,
8365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        String outputName,
8465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        Filter target,
8565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        String inputName) {
8665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (source == null || target == null) {
8765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new IllegalArgumentException("Passing null Filter in connect()!");
8865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else if (!containsFilter(source) || !containsFilter(target)) {
8965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new RuntimeException("Attempting to connect filter not in graph!");
9065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
9165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
9265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        OutputPort outPort = source.getOutputPort(outputName);
9365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        InputPort inPort = target.getInputPort(inputName);
9465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (outPort == null) {
9565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new RuntimeException("Unknown output port '" + outputName + "' on Filter " +
9665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                       source + "!");
9765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else if (inPort == null) {
9865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new RuntimeException("Unknown input port '" + inputName + "' on Filter " +
9965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                       target + "!");
10065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
10165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
10265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        preconnect(outPort, inPort);
10365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
10465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
10565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void connect(String sourceName,
10665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        String outputName,
10765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        String targetName,
10865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        String inputName) {
10965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        Filter source = getFilter(sourceName);
11065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        Filter target = getFilter(targetName);
11165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (source == null) {
11265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new RuntimeException(
11365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                "Attempting to connect unknown source filter '" + sourceName + "'!");
11465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else if (target == null) {
11565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new RuntimeException(
11665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                "Attempting to connect unknown target filter '" + targetName + "'!");
11765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
11865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        connect(source, outputName, target, inputName);
11965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
12065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
12165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public Set<Filter> getFilters() {
12265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return mFilters;
12365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
12465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
12565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void beginProcessing() {
12665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Opening all filter connections...");
12765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (Filter filter : mFilters) {
12865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            filter.openOutputs();
12965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
13065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mIsReady = true;
13165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
13265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
13365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void flushFrames() {
13465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (Filter filter : mFilters) {
13565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            filter.clearOutputs();
13665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
13765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
13865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
13965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void closeFilters(FilterContext context) {
14065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Closing all filters...");
14165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (Filter filter : mFilters) {
14265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            filter.performClose(context);
14365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
14465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mIsReady = false;
14565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
14665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
14765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public boolean isReady() {
14865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return mIsReady;
14965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
15065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
15165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void setAutoBranchMode(int autoBranchMode) {
15265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mAutoBranchMode = autoBranchMode;
15365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
15465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
15565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void setDiscardUnconnectedOutputs(boolean discard) {
15665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mDiscardUnconnectedOutputs = discard;
15765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
15865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
15965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void setTypeCheckMode(int typeCheckMode) {
16065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mTypeCheckMode = typeCheckMode;
16165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
16265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
16365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void tearDown(FilterContext context) {
16465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (!mFilters.isEmpty()) {
16565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            flushFrames();
16665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            for (Filter filter : mFilters) {
16765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                filter.performTearDown(context);
16865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
16965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mFilters.clear();
17065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mNameMap.clear();
17165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mIsReady = false;
17265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
17365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
17465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
17565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean readyForProcessing(Filter filter, Set<Filter> processed) {
17665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Check if this has been already processed
17765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (processed.contains(filter)) {
17865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            return false;
17965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
18065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
18165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Check if all dependencies have been processed
18265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (InputPort port : filter.getInputPorts()) {
18365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            Filter dependency = port.getSourceFilter();
18465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (dependency != null && !processed.contains(dependency)) {
18565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                return false;
18665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
18765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
18865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return true;
18965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
19065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
19165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void runTypeCheck() {
19265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        Stack<Filter> filterStack = new Stack<Filter>();
19365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        Set<Filter> processedFilters = new HashSet<Filter>();
19465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        filterStack.addAll(getSourceFilters());
19565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
19665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        while (!filterStack.empty()) {
19765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Get current filter and mark as processed
19865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            Filter filter = filterStack.pop();
19965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            processedFilters.add(filter);
20065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
20165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Anchor output formats
20265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            updateOutputs(filter);
20365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
20465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Perform type check
20565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "Running type check on " + filter + "...");
20665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            runTypeCheckOn(filter);
20765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
20865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Push connected filters onto stack
20965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            for (OutputPort port : filter.getOutputPorts()) {
21065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                Filter target = port.getTargetFilter();
21165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (target != null && readyForProcessing(target, processedFilters)) {
21265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    filterStack.push(target);
21365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
21465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
21565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
21665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
21765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Make sure all ports were setup
21865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (processedFilters.size() != getFilters().size()) {
21965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new RuntimeException("Could not schedule all filters! Is your graph malformed?");
22065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
22165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
22265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
22365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void updateOutputs(Filter filter) {
22465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (OutputPort outputPort : filter.getOutputPorts()) {
22565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            InputPort inputPort = outputPort.getBasePort();
22665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (inputPort != null) {
22765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                FrameFormat inputFormat = inputPort.getSourceFormat();
22865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                FrameFormat outputFormat = filter.getOutputFormat(outputPort.getName(),
22965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                                  inputFormat);
23065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (outputFormat == null) {
23165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    throw new RuntimeException("Filter did not return an output format for "
23265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        + outputPort + "!");
23365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
23465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                outputPort.setPortFormat(outputFormat);
23565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
23665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
23765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
23865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
23965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void runTypeCheckOn(Filter filter) {
24065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (InputPort inputPort : filter.getInputPorts()) {
24165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "Type checking port " + inputPort);
24265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            FrameFormat sourceFormat = inputPort.getSourceFormat();
24365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            FrameFormat targetFormat = inputPort.getPortFormat();
24465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (sourceFormat != null && targetFormat != null) {
24565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "Checking " + sourceFormat + " against " + targetFormat + ".");
24665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
24765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                boolean compatible = true;
24865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                switch (mTypeCheckMode) {
24965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    case TYPECHECK_OFF:
25065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        inputPort.setChecksType(false);
25165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        break;
25265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    case TYPECHECK_DYNAMIC:
25365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        compatible = sourceFormat.mayBeCompatibleWith(targetFormat);
25465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        inputPort.setChecksType(true);
25565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        break;
25665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    case TYPECHECK_STRICT:
25765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        compatible = sourceFormat.isCompatibleWith(targetFormat);
25865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        inputPort.setChecksType(false);
25965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        break;
26065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
26165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
26265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (!compatible) {
26365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    throw new RuntimeException("Type mismatch: Filter " + filter + " expects a "
26465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        + "format of type " + targetFormat + " but got a format of type "
26565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        + sourceFormat + "!");
26665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
26765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
26865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
26965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
27065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
27165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void checkConnections() {
27265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // TODO
27365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
27465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
27565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void discardUnconnectedOutputs() {
27665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Connect unconnected ports to Null filters
27765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        LinkedList<Filter> addedFilters = new LinkedList<Filter>();
27865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (Filter filter : mFilters) {
27965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            int id = 0;
28065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            for (OutputPort port : filter.getOutputPorts()) {
28165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (!port.isConnected()) {
28265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    if (mLogVerbose) Log.v(TAG, "Autoconnecting unconnected " + port + " to Null filter.");
28365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    NullFilter nullFilter = new NullFilter(filter.getName() + "ToNull" + id);
28465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    nullFilter.init();
28565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    addedFilters.add(nullFilter);
28665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    port.connectTo(nullFilter.getInputPort("frame"));
28765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    ++id;
28865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
28965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
29065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
29165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Add all added filters to this graph
29265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (Filter filter : addedFilters) {
29365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            addFilter(filter);
29465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
29565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
29665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
29765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void removeFilter(Filter filter) {
29865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mFilters.remove(filter);
29965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mNameMap.remove(filter.getName());
30065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
30165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
30265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void preconnect(OutputPort outPort, InputPort inPort) {
30365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        LinkedList<InputPort> targets;
30465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        targets = mPreconnections.get(outPort);
30565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (targets == null) {
30665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            targets = new LinkedList<InputPort>();
30765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mPreconnections.put(outPort, targets);
30865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
30965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        targets.add(inPort);
31065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
31165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
31265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void connectPorts() {
31365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int branchId = 1;
31465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (Entry<OutputPort, LinkedList<InputPort>> connection : mPreconnections.entrySet()) {
31565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            OutputPort outputPort = connection.getKey();
31665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            LinkedList<InputPort> inputPorts = connection.getValue();
31765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (inputPorts.size() == 1) {
31865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                outputPort.connectTo(inputPorts.get(0));
31965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else if (mAutoBranchMode == AUTOBRANCH_OFF) {
32065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new RuntimeException("Attempting to connect " + outputPort + " to multiple "
32165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                         + "filter ports! Enable auto-branching to allow this.");
32265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else {
32365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "Creating branch for " + outputPort + "!");
32465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                FrameBranch branch = null;
32565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mAutoBranchMode == AUTOBRANCH_SYNCED) {
32665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    branch = new FrameBranch("branch" + branchId++);
32765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                } else {
32865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    throw new RuntimeException("TODO: Unsynced branches not implemented yet!");
32965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
33065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                KeyValueMap branchParams = new KeyValueMap();
33165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                branch.initWithAssignmentList("outputs", inputPorts.size());
33265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                addFilter(branch);
33365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                outputPort.connectTo(branch.getInputPort("in"));
33465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                Iterator<InputPort> inputPortIter = inputPorts.iterator();
33565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                for (OutputPort branchOutPort : ((Filter)branch).getOutputPorts()) {
33665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    branchOutPort.connectTo(inputPortIter.next());
33765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
33865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
33965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
34065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mPreconnections.clear();
34165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
34265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
34365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private HashSet<Filter> getSourceFilters() {
34465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        HashSet<Filter> sourceFilters = new HashSet<Filter>();
34565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (Filter filter : getFilters()) {
34665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (filter.getNumberOfConnectedInputs() == 0) {
34765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (mLogVerbose) Log.v(TAG, "Found source filter: " + filter);
34865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                sourceFilters.add(filter);
34965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
35065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
35165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return sourceFilters;
35265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
35365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
35465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // Core internal methods /////////////////////////////////////////////////////////////////////////
35565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    void setupFilters() {
35665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mDiscardUnconnectedOutputs) {
35765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            discardUnconnectedOutputs();
35865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
35965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        connectPorts();
36065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        checkConnections();
36165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        runTypeCheck();
36265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
36365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn}
364