summaryrefslogtreecommitdiffstats
path: root/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
diff options
context:
space:
mode:
Diffstat (limited to 'media/mca/filterfw/java/android/filterfw/core/FilterGraph.java')
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FilterGraph.java363
1 files changed, 363 insertions, 0 deletions
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
new file mode 100644
index 0000000..12f7892
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Stack;
+
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.KeyValueMap;
+import android.filterpacks.base.FrameBranch;
+import android.filterpacks.base.NullFilter;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class FilterGraph {
+
+ private HashSet<Filter> mFilters = new HashSet<Filter>();
+ private HashMap<String, Filter> mNameMap = new HashMap<String, Filter>();
+ private HashMap<OutputPort, LinkedList<InputPort>> mPreconnections = new
+ HashMap<OutputPort, LinkedList<InputPort>>();
+
+ public static final int AUTOBRANCH_OFF = 0;
+ public static final int AUTOBRANCH_SYNCED = 1;
+ public static final int AUTOBRANCH_UNSYNCED = 2;
+
+ public static final int TYPECHECK_OFF = 0;
+ public static final int TYPECHECK_DYNAMIC = 1;
+ public static final int TYPECHECK_STRICT = 2;
+
+ private boolean mIsReady = false;
+ private int mAutoBranchMode = AUTOBRANCH_OFF;
+ private int mTypeCheckMode = TYPECHECK_STRICT;
+ private boolean mDiscardUnconnectedOutputs = false;
+
+ private boolean mLogVerbose;
+ private String TAG = "FilterGraph";
+
+ public FilterGraph() {
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ public boolean addFilter(Filter filter) {
+ if (!containsFilter(filter)) {
+ mFilters.add(filter);
+ mNameMap.put(filter.getName(), filter);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean containsFilter(Filter filter) {
+ return mFilters.contains(filter);
+ }
+
+ public Filter getFilter(String name) {
+ return mNameMap.get(name);
+ }
+
+ public void connect(Filter source,
+ String outputName,
+ Filter target,
+ String inputName) {
+ if (source == null || target == null) {
+ throw new IllegalArgumentException("Passing null Filter in connect()!");
+ } else if (!containsFilter(source) || !containsFilter(target)) {
+ throw new RuntimeException("Attempting to connect filter not in graph!");
+ }
+
+ OutputPort outPort = source.getOutputPort(outputName);
+ InputPort inPort = target.getInputPort(inputName);
+ if (outPort == null) {
+ throw new RuntimeException("Unknown output port '" + outputName + "' on Filter " +
+ source + "!");
+ } else if (inPort == null) {
+ throw new RuntimeException("Unknown input port '" + inputName + "' on Filter " +
+ target + "!");
+ }
+
+ preconnect(outPort, inPort);
+ }
+
+ public void connect(String sourceName,
+ String outputName,
+ String targetName,
+ String inputName) {
+ Filter source = getFilter(sourceName);
+ Filter target = getFilter(targetName);
+ if (source == null) {
+ throw new RuntimeException(
+ "Attempting to connect unknown source filter '" + sourceName + "'!");
+ } else if (target == null) {
+ throw new RuntimeException(
+ "Attempting to connect unknown target filter '" + targetName + "'!");
+ }
+ connect(source, outputName, target, inputName);
+ }
+
+ public Set<Filter> getFilters() {
+ return mFilters;
+ }
+
+ public void beginProcessing() {
+ if (mLogVerbose) Log.v(TAG, "Opening all filter connections...");
+ for (Filter filter : mFilters) {
+ filter.openOutputs();
+ }
+ mIsReady = true;
+ }
+
+ public void flushFrames() {
+ for (Filter filter : mFilters) {
+ filter.clearOutputs();
+ }
+ }
+
+ public void closeFilters(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Closing all filters...");
+ for (Filter filter : mFilters) {
+ filter.performClose(context);
+ }
+ mIsReady = false;
+ }
+
+ public boolean isReady() {
+ return mIsReady;
+ }
+
+ public void setAutoBranchMode(int autoBranchMode) {
+ mAutoBranchMode = autoBranchMode;
+ }
+
+ public void setDiscardUnconnectedOutputs(boolean discard) {
+ mDiscardUnconnectedOutputs = discard;
+ }
+
+ public void setTypeCheckMode(int typeCheckMode) {
+ mTypeCheckMode = typeCheckMode;
+ }
+
+ public void tearDown(FilterContext context) {
+ if (!mFilters.isEmpty()) {
+ flushFrames();
+ for (Filter filter : mFilters) {
+ filter.performTearDown(context);
+ }
+ mFilters.clear();
+ mNameMap.clear();
+ mIsReady = false;
+ }
+ }
+
+ private boolean readyForProcessing(Filter filter, Set<Filter> processed) {
+ // Check if this has been already processed
+ if (processed.contains(filter)) {
+ return false;
+ }
+
+ // Check if all dependencies have been processed
+ for (InputPort port : filter.getInputPorts()) {
+ Filter dependency = port.getSourceFilter();
+ if (dependency != null && !processed.contains(dependency)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void runTypeCheck() {
+ Stack<Filter> filterStack = new Stack<Filter>();
+ Set<Filter> processedFilters = new HashSet<Filter>();
+ filterStack.addAll(getSourceFilters());
+
+ while (!filterStack.empty()) {
+ // Get current filter and mark as processed
+ Filter filter = filterStack.pop();
+ processedFilters.add(filter);
+
+ // Anchor output formats
+ updateOutputs(filter);
+
+ // Perform type check
+ if (mLogVerbose) Log.v(TAG, "Running type check on " + filter + "...");
+ runTypeCheckOn(filter);
+
+ // Push connected filters onto stack
+ for (OutputPort port : filter.getOutputPorts()) {
+ Filter target = port.getTargetFilter();
+ if (target != null && readyForProcessing(target, processedFilters)) {
+ filterStack.push(target);
+ }
+ }
+ }
+
+ // Make sure all ports were setup
+ if (processedFilters.size() != getFilters().size()) {
+ throw new RuntimeException("Could not schedule all filters! Is your graph malformed?");
+ }
+ }
+
+ private void updateOutputs(Filter filter) {
+ for (OutputPort outputPort : filter.getOutputPorts()) {
+ InputPort inputPort = outputPort.getBasePort();
+ if (inputPort != null) {
+ FrameFormat inputFormat = inputPort.getSourceFormat();
+ FrameFormat outputFormat = filter.getOutputFormat(outputPort.getName(),
+ inputFormat);
+ if (outputFormat == null) {
+ throw new RuntimeException("Filter did not return an output format for "
+ + outputPort + "!");
+ }
+ outputPort.setPortFormat(outputFormat);
+ }
+ }
+ }
+
+ private void runTypeCheckOn(Filter filter) {
+ for (InputPort inputPort : filter.getInputPorts()) {
+ if (mLogVerbose) Log.v(TAG, "Type checking port " + inputPort);
+ FrameFormat sourceFormat = inputPort.getSourceFormat();
+ FrameFormat targetFormat = inputPort.getPortFormat();
+ if (sourceFormat != null && targetFormat != null) {
+ if (mLogVerbose) Log.v(TAG, "Checking " + sourceFormat + " against " + targetFormat + ".");
+
+ boolean compatible = true;
+ switch (mTypeCheckMode) {
+ case TYPECHECK_OFF:
+ inputPort.setChecksType(false);
+ break;
+ case TYPECHECK_DYNAMIC:
+ compatible = sourceFormat.mayBeCompatibleWith(targetFormat);
+ inputPort.setChecksType(true);
+ break;
+ case TYPECHECK_STRICT:
+ compatible = sourceFormat.isCompatibleWith(targetFormat);
+ inputPort.setChecksType(false);
+ break;
+ }
+
+ if (!compatible) {
+ throw new RuntimeException("Type mismatch: Filter " + filter + " expects a "
+ + "format of type " + targetFormat + " but got a format of type "
+ + sourceFormat + "!");
+ }
+ }
+ }
+ }
+
+ private void checkConnections() {
+ // TODO
+ }
+
+ private void discardUnconnectedOutputs() {
+ // Connect unconnected ports to Null filters
+ LinkedList<Filter> addedFilters = new LinkedList<Filter>();
+ for (Filter filter : mFilters) {
+ int id = 0;
+ for (OutputPort port : filter.getOutputPorts()) {
+ if (!port.isConnected()) {
+ if (mLogVerbose) Log.v(TAG, "Autoconnecting unconnected " + port + " to Null filter.");
+ NullFilter nullFilter = new NullFilter(filter.getName() + "ToNull" + id);
+ nullFilter.init();
+ addedFilters.add(nullFilter);
+ port.connectTo(nullFilter.getInputPort("frame"));
+ ++id;
+ }
+ }
+ }
+ // Add all added filters to this graph
+ for (Filter filter : addedFilters) {
+ addFilter(filter);
+ }
+ }
+
+ private void removeFilter(Filter filter) {
+ mFilters.remove(filter);
+ mNameMap.remove(filter.getName());
+ }
+
+ private void preconnect(OutputPort outPort, InputPort inPort) {
+ LinkedList<InputPort> targets;
+ targets = mPreconnections.get(outPort);
+ if (targets == null) {
+ targets = new LinkedList<InputPort>();
+ mPreconnections.put(outPort, targets);
+ }
+ targets.add(inPort);
+ }
+
+ private void connectPorts() {
+ int branchId = 1;
+ for (Entry<OutputPort, LinkedList<InputPort>> connection : mPreconnections.entrySet()) {
+ OutputPort outputPort = connection.getKey();
+ LinkedList<InputPort> inputPorts = connection.getValue();
+ if (inputPorts.size() == 1) {
+ outputPort.connectTo(inputPorts.get(0));
+ } else if (mAutoBranchMode == AUTOBRANCH_OFF) {
+ throw new RuntimeException("Attempting to connect " + outputPort + " to multiple "
+ + "filter ports! Enable auto-branching to allow this.");
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Creating branch for " + outputPort + "!");
+ FrameBranch branch = null;
+ if (mAutoBranchMode == AUTOBRANCH_SYNCED) {
+ branch = new FrameBranch("branch" + branchId++);
+ } else {
+ throw new RuntimeException("TODO: Unsynced branches not implemented yet!");
+ }
+ KeyValueMap branchParams = new KeyValueMap();
+ branch.initWithAssignmentList("outputs", inputPorts.size());
+ addFilter(branch);
+ outputPort.connectTo(branch.getInputPort("in"));
+ Iterator<InputPort> inputPortIter = inputPorts.iterator();
+ for (OutputPort branchOutPort : ((Filter)branch).getOutputPorts()) {
+ branchOutPort.connectTo(inputPortIter.next());
+ }
+ }
+ }
+ mPreconnections.clear();
+ }
+
+ private HashSet<Filter> getSourceFilters() {
+ HashSet<Filter> sourceFilters = new HashSet<Filter>();
+ for (Filter filter : getFilters()) {
+ if (filter.getNumberOfConnectedInputs() == 0) {
+ if (mLogVerbose) Log.v(TAG, "Found source filter: " + filter);
+ sourceFilters.add(filter);
+ }
+ }
+ return sourceFilters;
+ }
+
+ // Core internal methods /////////////////////////////////////////////////////////////////////////
+ void setupFilters() {
+ if (mDiscardUnconnectedOutputs) {
+ discardUnconnectedOutputs();
+ }
+ connectPorts();
+ checkConnections();
+ runTypeCheck();
+ }
+}