Merge "Multi-Project Commit: Move of filterfw out of system/media (2 of 7)"

This commit is contained in:
Marius Renn
2012-03-27 16:29:42 -07:00
committed by Android (Google) Code Review
246 changed files with 31005 additions and 5 deletions

View File

@@ -324,9 +324,6 @@ fwbase_dirs_to_document := \
# include definition of libcore_to_document
include $(LOCAL_PATH)/../../libcore/Docs.mk
# include definition of libfilterfw_to_document
include $(LOCAL_PATH)/../../system/media/mca/Docs.mk
non_base_dirs := \
../../external/apache-http/src/org/apache/http
@@ -349,8 +346,7 @@ html_dirs := \
# Common sources for doc check and api check
common_src_files := \
$(call find-other-html-files, $(html_dirs)) \
$(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore)) \
$(addprefix ../../system/media/mca/, $(call libfilterfw_to_document, $(LOCAL_PATH)/../../system/media/mca)) \
$(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore))
# These are relative to frameworks/base
framework_docs_LOCAL_SRC_FILES := \

21
media/mca/Android.mk Normal file
View File

@@ -0,0 +1,21 @@
# 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.
#
#
# Build all native libraries
#
include $(call all-subdir-makefiles)

View File

@@ -0,0 +1,111 @@
/*
* 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.media.effect;
/**
* <p>Effects are high-performance transformations that can be applied to image frames. These are
* passed in the form of OpenGL ES 2.0 texture names. Typical frames could be images loaded from
* disk, or frames from the camera or other video streams.</p>
*
* <p>To create an Effect you must first create an EffectContext. You can obtain an instance of the
* context's EffectFactory by calling
* {@link android.media.effect.EffectContext#getFactory() getFactory()}. The EffectFactory allows
* you to instantiate specific Effects.</p>
*
* <p>The application is responsible for creating an EGL context, and making it current before
* applying an effect. An effect is bound to a single EffectContext, which in turn is bound to a
* single EGL context. If your EGL context is destroyed, the EffectContext becomes invalid and any
* effects bound to this context can no longer be used.</p>
*
*/
public abstract class Effect {
/**
* Get the effect name.
*
* Returns the unique name of the effect, which matches the name used for instantiating this
* effect by the EffectFactory.
*
* @return The name of the effect.
*/
public abstract String getName();
/**
* Apply an effect to GL textures.
*
* <p>Apply the Effect on the specified input GL texture, and write the result into the
* output GL texture. The texture names passed must be valid in the current GL context.</p>
*
* <p>The input texture must be a valid texture name with the given width and height and must be
* bound to a GL_TEXTURE_2D texture image (usually done by calling the glTexImage2D() function).
* Multiple mipmap levels may be provided.</p>
*
* <p>If the output texture has not been bound to a texture image, it will be automatically
* bound by the effect as a GL_TEXTURE_2D. It will contain one mipmap level (0), which will have
* the same size as the input. No other mipmap levels are defined. If the output texture was
* bound already, and its size does not match the input texture size, the result may be clipped
* or only partially fill the texture.</p>
*
* <p>Note, that regardless of whether a texture image was originally provided or not, both the
* input and output textures are owned by the caller. That is, the caller is responsible for
* calling glDeleteTextures() to deallocate the input and output textures.</p>
*
* @param inputTexId The GL texture name of a valid and bound input texture.
* @param width The width of the input texture in pixels.
* @param height The height of the input texture in pixels.
* @param outputTexId The GL texture name of the output texture.
*/
public abstract void apply(int inputTexId, int width, int height, int outputTexId);
/**
* Set a filter parameter.
*
* Consult the effect documentation for a list of supported parameter keys for each effect.
*
* @param parameterKey The name of the parameter to adjust.
* @param value The new value to set the parameter to.
* @throws InvalidArgumentException if parameterName is not a recognized name, or the value is
* not a valid value for this parameter.
*/
public abstract void setParameter(String parameterKey, Object value);
/**
* Set an effect listener.
*
* Some effects may report state changes back to the host, if a listener is set. Consult the
* individual effect documentation for more details.
*
* @param listener The listener to receive update callbacks on.
*/
public void setUpdateListener(EffectUpdateListener listener) {
}
/**
* Release an effect.
*
* <p>Releases the effect and any resources associated with it. You may call this if you need to
* make sure acquired resources are no longer held by the effect. Releasing an effect makes it
* invalid for reuse.</p>
*
* <p>Note that this method must be called with the EffectContext and EGL context current, as
* the effect may release internal GL resources.</p>
*/
public abstract void release();
}

View File

@@ -0,0 +1,131 @@
/*
* 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.media.effect;
import android.filterfw.core.CachedFrameManager;
import android.filterfw.core.FilterContext;
import android.filterfw.core.FilterFactory;
import android.filterfw.core.GLEnvironment;
import android.filterfw.core.GLFrame;
import android.filterfw.core.FrameManager;
import android.opengl.GLES20;
/**
* <p>An EffectContext keeps all necessary state information to run Effects within a Open GL ES 2.0
* context.</p>
*
* <p>Every EffectContext is bound to one GL context. The application is responsible for creating
* this EGL context, and making it current before applying any effect. If your EGL context is
* destroyed, the EffectContext becomes invalid and any effects bound to this context can no longer
* be used. If you switch to another EGL context, you must create a new EffectContext. Each Effect
* is bound to a single EffectContext, and can only be executed in that context.</p>
*/
public class EffectContext {
private final int GL_STATE_FBO = 0;
private final int GL_STATE_PROGRAM = 1;
private final int GL_STATE_ARRAYBUFFER = 2;
private final int GL_STATE_COUNT = 3;
FilterContext mFilterContext;
private EffectFactory mFactory;
private int[] mOldState = new int[GL_STATE_COUNT];
/**
* Creates a context within the current GL context.
*
* <p>Binds the EffectContext to the current OpenGL context. All subsequent calls to the
* EffectContext must be made in the GL context that was active during creation.
* When you have finished using a context, you must call {@link #release()}. to dispose of all
* resources associated with this context.</p>
*/
public static EffectContext createWithCurrentGlContext() {
EffectContext result = new EffectContext();
result.initInCurrentGlContext();
return result;
}
/**
* Returns the EffectFactory for this context.
*
* <p>The EffectFactory returned from this method allows instantiating new effects within this
* context.</p>
*
* @return The EffectFactory instance for this context.
*/
public EffectFactory getFactory() {
return mFactory;
}
/**
* Releases the context.
*
* <p>Releases all the resources and effects associated with the EffectContext. This renders the
* context and all the effects bound to this context invalid. You must no longer use the context
* or any of its bound effects after calling release().</p>
*
* <p>Note that this method must be called with the proper EGL context made current, as the
* EffectContext and its effects may release internal GL resources.</p>
*/
public void release() {
mFilterContext.tearDown();
mFilterContext = null;
}
private EffectContext() {
mFilterContext = new FilterContext();
mFilterContext.setFrameManager(new CachedFrameManager());
mFactory = new EffectFactory(this);
}
private void initInCurrentGlContext() {
if (!GLEnvironment.isAnyContextActive()) {
throw new RuntimeException("Attempting to initialize EffectContext with no active "
+ "GL context!");
}
GLEnvironment glEnvironment = new GLEnvironment();
glEnvironment.initWithCurrentContext();
mFilterContext.initGLEnvironment(glEnvironment);
}
final void assertValidGLState() {
GLEnvironment glEnv = mFilterContext.getGLEnvironment();
if (glEnv == null || !glEnv.isContextActive()) {
if (GLEnvironment.isAnyContextActive()) {
throw new RuntimeException("Applying effect in wrong GL context!");
} else {
throw new RuntimeException("Attempting to apply effect without valid GL context!");
}
}
}
final void saveGLState() {
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, mOldState, GL_STATE_FBO);
GLES20.glGetIntegerv(GLES20.GL_CURRENT_PROGRAM, mOldState, GL_STATE_PROGRAM);
GLES20.glGetIntegerv(GLES20.GL_ARRAY_BUFFER_BINDING, mOldState, GL_STATE_ARRAYBUFFER);
}
final void restoreGLState() {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mOldState[GL_STATE_FBO]);
GLES20.glUseProgram(mOldState[GL_STATE_PROGRAM]);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mOldState[GL_STATE_ARRAYBUFFER]);
}
}

View File

@@ -0,0 +1,517 @@
/*
* 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.media.effect;
import java.lang.reflect.Constructor;
import java.util.HashMap;
/**
* <p>The EffectFactory class defines the list of available Effects, and provides functionality to
* inspect and instantiate them. Some effects may not be available on all platforms, so before
* creating a certain effect, the application should confirm that the effect is supported on this
* platform by calling {@link #isEffectSupported(String)}.</p>
*/
public class EffectFactory {
private EffectContext mEffectContext;
private final static String[] EFFECT_PACKAGES = {
"android.media.effect.effects.", // Default effect package
"" // Allows specifying full class path
};
/** List of Effects */
/**
* <p>Copies the input texture to the output.</p>
* <p>Available parameters: None</p>
* @hide
*/
public final static String EFFECT_IDENTITY = "IdentityEffect";
/**
* <p>Adjusts the brightness of the image.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>brightness</code></td>
* <td>The brightness multiplier.</td>
* <td>Positive float. 1.0 means no change;
larger values will increase brightness.</td>
* </tr>
* </table>
*/
public final static String EFFECT_BRIGHTNESS =
"android.media.effect.effects.BrightnessEffect";
/**
* <p>Adjusts the contrast of the image.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>contrast</code></td>
* <td>The contrast multiplier.</td>
* <td>Float. 1.0 means no change;
larger values will increase contrast.</td>
* </tr>
* </table>
*/
public final static String EFFECT_CONTRAST =
"android.media.effect.effects.ContrastEffect";
/**
* <p>Applies a fisheye lens distortion to the image.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>scale</code></td>
* <td>The scale of the distortion.</td>
* <td>Float, between 0 and 1. Zero means no distortion.</td>
* </tr>
* </table>
*/
public final static String EFFECT_FISHEYE =
"android.media.effect.effects.FisheyeEffect";
/**
* <p>Replaces the background of the input frames with frames from a
* selected video. Requires an initial learning period with only the
* background visible before the effect becomes active. The effect will wait
* until it does not see any motion in the scene before learning the
* background and starting the effect.</p>
*
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>source</code></td>
* <td>A URI for the background video to use. This parameter must be
* supplied before calling apply() for the first time.</td>
* <td>String, such as from
* {@link android.net.Uri#toString Uri.toString()}</td>
* </tr>
* </table>
*
* <p>If the update listener is set for this effect using
* {@link Effect#setUpdateListener}, it will be called when the effect has
* finished learning the background, with a null value for the info
* parameter.</p>
*/
public final static String EFFECT_BACKDROPPER =
"android.media.effect.effects.BackDropperEffect";
/**
* <p>Attempts to auto-fix the image based on histogram equalization.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>scale</code></td>
* <td>The scale of the adjustment.</td>
* <td>Float, between 0 and 1. Zero means no adjustment, while 1 indicates the maximum
* amount of adjustment.</td>
* </tr>
* </table>
*/
public final static String EFFECT_AUTOFIX =
"android.media.effect.effects.AutoFixEffect";
/**
* <p>Adjusts the range of minimal and maximal color pixel intensities.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>black</code></td>
* <td>The value of the minimal pixel.</td>
* <td>Float, between 0 and 1.</td>
* </tr>
* <tr><td><code>white</code></td>
* <td>The value of the maximal pixel.</td>
* <td>Float, between 0 and 1.</td>
* </tr>
* </table>
*/
public final static String EFFECT_BLACKWHITE =
"android.media.effect.effects.BlackWhiteEffect";
/**
* <p>Crops an upright rectangular area from the image. If the crop region falls outside of
* the image bounds, the results are undefined.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>xorigin</code></td>
* <td>The origin's x-value.</td>
* <td>Integer, between 0 and width of the image.</td>
* </tr>
* <tr><td><code>yorigin</code></td>
* <td>The origin's y-value.</td>
* <td>Integer, between 0 and height of the image.</td>
* </tr>
* <tr><td><code>width</code></td>
* <td>The width of the cropped image.</td>
* <td>Integer, between 1 and the width of the image minus xorigin.</td>
* </tr>
* <tr><td><code>height</code></td>
* <td>The height of the cropped image.</td>
* <td>Integer, between 1 and the height of the image minus yorigin.</td>
* </tr>
* </table>
*/
public final static String EFFECT_CROP =
"android.media.effect.effects.CropEffect";
/**
* <p>Applies a cross process effect on image, in which the red and green channels are
* enhanced while the blue channel is restricted.</p>
* <p>Available parameters: None</p>
*/
public final static String EFFECT_CROSSPROCESS =
"android.media.effect.effects.CrossProcessEffect";
/**
* <p>Applies black and white documentary style effect on image..</p>
* <p>Available parameters: None</p>
*/
public final static String EFFECT_DOCUMENTARY =
"android.media.effect.effects.DocumentaryEffect";
/**
* <p>Overlays a bitmap (with premultiplied alpha channel) onto the input image. The bitmap
* is stretched to fit the input image.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>bitmap</code></td>
* <td>The overlay bitmap.</td>
* <td>A non-null Bitmap instance.</td>
* </tr>
* </table>
*/
public final static String EFFECT_BITMAPOVERLAY =
"android.media.effect.effects.BitmapOverlayEffect";
/**
* <p>Representation of photo using only two color tones.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>first_color</code></td>
* <td>The first color tone.</td>
* <td>Integer, representing an ARGB color with 8 bits per channel. May be created using
* {@link android.graphics.Color Color} class.</td>
* </tr>
* <tr><td><code>second_color</code></td>
* <td>The second color tone.</td>
* <td>Integer, representing an ARGB color with 8 bits per channel. May be created using
* {@link android.graphics.Color Color} class.</td>
* </tr>
* </table>
*/
public final static String EFFECT_DUOTONE =
"android.media.effect.effects.DuotoneEffect";
/**
* <p>Applies back-light filling to the image.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>strength</code></td>
* <td>The strength of the backlight.</td>
* <td>Float, between 0 and 1. Zero means no change.</td>
* </tr>
* </table>
*/
public final static String EFFECT_FILLLIGHT =
"android.media.effect.effects.FillLightEffect";
/**
* <p>Flips image vertically and/or horizontally.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>vertical</code></td>
* <td>Whether to flip image vertically.</td>
* <td>Boolean</td>
* </tr>
* <tr><td><code>horizontal</code></td>
* <td>Whether to flip image horizontally.</td>
* <td>Boolean</td>
* </tr>
* </table>
*/
public final static String EFFECT_FLIP =
"android.media.effect.effects.FlipEffect";
/**
* <p>Applies film grain effect to image.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>strength</code></td>
* <td>The strength of the grain effect.</td>
* <td>Float, between 0 and 1. Zero means no change.</td>
* </tr>
* </table>
*/
public final static String EFFECT_GRAIN =
"android.media.effect.effects.GrainEffect";
/**
* <p>Converts image to grayscale.</p>
* <p>Available parameters: None</p>
*/
public final static String EFFECT_GRAYSCALE =
"android.media.effect.effects.GrayscaleEffect";
/**
* <p>Applies lomo-camera style effect to image.</p>
* <p>Available parameters: None</p>
*/
public final static String EFFECT_LOMOISH =
"android.media.effect.effects.LomoishEffect";
/**
* <p>Inverts the image colors.</p>
* <p>Available parameters: None</p>
*/
public final static String EFFECT_NEGATIVE =
"android.media.effect.effects.NegativeEffect";
/**
* <p>Applies posterization effect to image.</p>
* <p>Available parameters: None</p>
*/
public final static String EFFECT_POSTERIZE =
"android.media.effect.effects.PosterizeEffect";
/**
* <p>Removes red eyes on specified region.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>centers</code></td>
* <td>Multiple center points (x, y) of the red eye regions.</td>
* <td>An array of floats, where (f[2*i], f[2*i+1]) specifies the center of the i'th eye.
* Coordinate values are expected to be normalized between 0 and 1.</td>
* </tr>
* </table>
*/
public final static String EFFECT_REDEYE =
"android.media.effect.effects.RedEyeEffect";
/**
* <p>Rotates the image. The output frame size must be able to fit the rotated version of
* the input image. Note that the rotation snaps to a the closest multiple of 90 degrees.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>angle</code></td>
* <td>The angle of rotation in degrees.</td>
* <td>Integer value. This will be rounded to the nearest multiple of 90.</td>
* </tr>
* </table>
*/
public final static String EFFECT_ROTATE =
"android.media.effect.effects.RotateEffect";
/**
* <p>Adjusts color saturation of image.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>scale</code></td>
* <td>The scale of color saturation.</td>
* <td>Float, between -1 and 1. 0 means no change, while -1 indicates full desaturation,
* i.e. grayscale.</td>
* </tr>
* </table>
*/
public final static String EFFECT_SATURATE =
"android.media.effect.effects.SaturateEffect";
/**
* <p>Converts image to sepia tone.</p>
* <p>Available parameters: None</p>
*/
public final static String EFFECT_SEPIA =
"android.media.effect.effects.SepiaEffect";
/**
* <p>Sharpens the image.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>scale</code></td>
* <td>The degree of sharpening.</td>
* <td>Float, between 0 and 1. 0 means no change.</td>
* </tr>
* </table>
*/
public final static String EFFECT_SHARPEN =
"android.media.effect.effects.SharpenEffect";
/**
* <p>Rotates the image according to the specified angle, and crops the image so that no
* non-image portions are visible.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>angle</code></td>
* <td>The angle of rotation.</td>
* <td>Float, between -45 and +45.</td>
* </tr>
* </table>
*/
public final static String EFFECT_STRAIGHTEN =
"android.media.effect.effects.StraightenEffect";
/**
* <p>Adjusts color temperature of the image.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>scale</code></td>
* <td>The value of color temperature.</td>
* <td>Float, between 0 and 1, with 0 indicating cool, and 1 indicating warm. A value of
* of 0.5 indicates no change.</td>
* </tr>
* </table>
*/
public final static String EFFECT_TEMPERATURE =
"android.media.effect.effects.ColorTemperatureEffect";
/**
* <p>Tints the photo with specified color.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>tint</code></td>
* <td>The color of the tint.</td>
* <td>Integer, representing an ARGB color with 8 bits per channel. May be created using
* {@link android.graphics.Color Color} class.</td>
* </tr>
* </table>
*/
public final static String EFFECT_TINT =
"android.media.effect.effects.TintEffect";
/**
* <p>Adds a vignette effect to image, i.e. fades away the outer image edges.</p>
* <p>Available parameters:</p>
* <table>
* <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
* <tr><td><code>scale</code></td>
* <td>The scale of vignetting.</td>
* <td>Float, between 0 and 1. 0 means no change.</td>
* </tr>
* </table>
*/
public final static String EFFECT_VIGNETTE =
"android.media.effect.effects.VignetteEffect";
EffectFactory(EffectContext effectContext) {
mEffectContext = effectContext;
}
/**
* Instantiate a new effect with the given effect name.
*
* <p>The effect's parameters will be set to their default values.</p>
*
* <p>Note that the EGL context associated with the current EffectContext need not be made
* current when creating an effect. This allows the host application to instantiate effects
* before any EGL context has become current.</p>
*
* @param effectName The name of the effect to create.
* @return A new Effect instance.
* @throws IllegalArgumentException if the effect with the specified name is not supported or
* not known.
*/
public Effect createEffect(String effectName) {
Class effectClass = getEffectClassByName(effectName);
if (effectClass == null) {
throw new IllegalArgumentException("Cannot instantiate unknown effect '" +
effectName + "'!");
}
return instantiateEffect(effectClass, effectName);
}
/**
* Check if an effect is supported on this platform.
*
* <p>Some effects may only be available on certain platforms. Use this method before
* instantiating an effect to make sure it is supported.</p>
*
* @param effectName The name of the effect.
* @return true, if the effect is supported on this platform.
* @throws IllegalArgumentException if the effect name is not known.
*/
public static boolean isEffectSupported(String effectName) {
return getEffectClassByName(effectName) != null;
}
private static Class getEffectClassByName(String className) {
Class effectClass = null;
// Get context's classloader; otherwise cannot load non-framework effects
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
// Look for the class in the imported packages
for (String packageName : EFFECT_PACKAGES) {
try {
effectClass = contextClassLoader.loadClass(packageName + className);
} catch (ClassNotFoundException e) {
continue;
}
// Exit loop if class was found.
if (effectClass != null) {
break;
}
}
return effectClass;
}
private Effect instantiateEffect(Class effectClass, String name) {
// Make sure this is an Effect subclass
try {
effectClass.asSubclass(Effect.class);
} catch (ClassCastException e) {
throw new IllegalArgumentException("Attempting to allocate effect '" + effectClass
+ "' which is not a subclass of Effect!", e);
}
// Look for the correct constructor
Constructor effectConstructor = null;
try {
effectConstructor = effectClass.getConstructor(EffectContext.class, String.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException("The effect class '" + effectClass + "' does not have "
+ "the required constructor.", e);
}
// Construct the effect
Effect effect = null;
try {
effect = (Effect)effectConstructor.newInstance(mEffectContext, name);
} catch (Throwable t) {
throw new RuntimeException("There was an error constructing the effect '" + effectClass
+ "'!", t);
}
return effect;
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.media.effect;
/**
* Some effects may issue callbacks to inform the host of changes to the effect state. This is the
* listener interface for receiving those callbacks.
*/
public interface EffectUpdateListener {
/**
* Called when the effect state is updated.
*
* @param effect The effect that has been updated.
* @param info A value that gives more information about the update. See the effect's
* documentation for more details on what this object is.
*/
public void onEffectUpdated(Effect effect, Object info);
}

View File

@@ -0,0 +1,101 @@
/*
* 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.media.effect;
import android.filterfw.core.CachedFrameManager;
import android.filterfw.core.FilterContext;
import android.filterfw.core.FilterFactory;
import android.filterfw.core.GLEnvironment;
import android.filterfw.core.GLFrame;
import android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.FrameManager;
import android.filterfw.format.ImageFormat;
/**
* The FilterEffect class is the base class for all Effects based on Filters from the Mobile
* Filter Framework (MFF).
* @hide
*/
public abstract class FilterEffect extends Effect {
protected EffectContext mEffectContext;
private String mName;
/**
* Protected constructor as FilterEffects should be created by Factory.
*/
protected FilterEffect(EffectContext context, String name) {
mEffectContext = context;
mName = name;
}
/**
* Get the effect name.
*
* Returns the unique name of the effect, which matches the name used for instantiating this
* effect by the EffectFactory.
*
* @return The name of the effect.
*/
@Override
public String getName() {
return mName;
}
// Helper Methods for subclasses ///////////////////////////////////////////////////////////////
/**
* Call this before manipulating the GL context. Will assert that the GL environment is in a
* valid state, and save it.
*/
protected void beginGLEffect() {
mEffectContext.assertValidGLState();
mEffectContext.saveGLState();
}
/**
* Call this after manipulating the GL context. Restores the previous GL state.
*/
protected void endGLEffect() {
mEffectContext.restoreGLState();
}
/**
* Returns the active filter context for this effect.
*/
protected FilterContext getFilterContext() {
return mEffectContext.mFilterContext;
}
/**
* Converts a texture into a Frame.
*/
protected Frame frameFromTexture(int texId, int width, int height) {
FrameManager manager = getFilterContext().getFrameManager();
FrameFormat format = ImageFormat.create(width, height,
ImageFormat.COLORSPACE_RGBA,
FrameFormat.TARGET_GPU);
Frame frame = manager.newBoundFrame(format,
GLFrame.EXISTING_TEXTURE_BINDING,
texId);
frame.setTimestamp(Frame.TIMESTAMP_UNKNOWN);
return frame;
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.media.effect;
import android.filterfw.core.Filter;
import android.filterfw.core.FilterGraph;
import android.filterfw.core.GraphRunner;
import android.filterfw.core.SimpleScheduler;
import android.filterfw.core.SyncRunner;
import android.media.effect.Effect;
import android.media.effect.FilterEffect;
import android.media.effect.EffectContext;
import android.filterfw.io.GraphIOException;
import android.filterfw.io.GraphReader;
import android.filterfw.io.TextGraphReader;
import android.util.Log;
/**
* Effect subclass for effects based on a single Filter. Subclasses need only invoke the
* constructor with the correct arguments to obtain an Effect implementation.
*
* @hide
*/
public class FilterGraphEffect extends FilterEffect {
private static final String TAG = "FilterGraphEffect";
protected String mInputName;
protected String mOutputName;
protected GraphRunner mRunner;
protected FilterGraph mGraph;
protected Class mSchedulerClass;
/**
* Constructs a new FilterGraphEffect.
*
* @param name The name of this effect (used to create it in the EffectFactory).
* @param graphString The graph string to create the graph.
* @param inputName The name of the input GLTextureSource filter.
* @param outputName The name of the output GLTextureSource filter.
*/
public FilterGraphEffect(EffectContext context,
String name,
String graphString,
String inputName,
String outputName,
Class scheduler) {
super(context, name);
mInputName = inputName;
mOutputName = outputName;
mSchedulerClass = scheduler;
createGraph(graphString);
}
private void createGraph(String graphString) {
GraphReader reader = new TextGraphReader();
try {
mGraph = reader.readGraphString(graphString);
} catch (GraphIOException e) {
throw new RuntimeException("Could not setup effect", e);
}
if (mGraph == null) {
throw new RuntimeException("Could not setup effect");
}
mRunner = new SyncRunner(getFilterContext(), mGraph, mSchedulerClass);
}
@Override
public void apply(int inputTexId, int width, int height, int outputTexId) {
beginGLEffect();
Filter src = mGraph.getFilter(mInputName);
if (src != null) {
src.setInputValue("texId", inputTexId);
src.setInputValue("width", width);
src.setInputValue("height", height);
} else {
throw new RuntimeException("Internal error applying effect");
}
Filter dest = mGraph.getFilter(mOutputName);
if (dest != null) {
dest.setInputValue("texId", outputTexId);
} else {
throw new RuntimeException("Internal error applying effect");
}
try {
mRunner.run();
} catch (RuntimeException e) {
throw new RuntimeException("Internal error applying effect: ", e);
}
endGLEffect();
}
@Override
public void setParameter(String parameterKey, Object value) {
}
@Override
public void release() {
mGraph.tearDown(getFilterContext());
mGraph = null;
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.media.effect;
import android.filterfw.core.Filter;
import android.filterfw.core.FilterFactory;
import android.filterfw.core.FilterFunction;
import android.filterfw.core.Frame;
import android.media.effect.Effect;
import android.media.effect.EffectContext;
import android.util.Log;
/**
* Effect subclass for effects based on a single Filter. Subclasses need only invoke the
* constructor with the correct arguments to obtain an Effect implementation.
*
* @hide
*/
public class SingleFilterEffect extends FilterEffect {
protected FilterFunction mFunction;
protected String mInputName;
protected String mOutputName;
/**
* Constructs a new FilterFunctionEffect.
*
* @param name The name of this effect (used to create it in the EffectFactory).
* @param filterClass The class of the filter to wrap.
* @param inputName The name of the input image port.
* @param outputName The name of the output image port.
* @param finalParameters Key-value pairs of final input port assignments.
*/
public SingleFilterEffect(EffectContext context,
String name,
Class filterClass,
String inputName,
String outputName,
Object... finalParameters) {
super(context, name);
mInputName = inputName;
mOutputName = outputName;
String filterName = filterClass.getSimpleName();
FilterFactory factory = FilterFactory.sharedFactory();
Filter filter = factory.createFilterByClass(filterClass, filterName);
filter.initWithAssignmentList(finalParameters);
mFunction = new FilterFunction(getFilterContext(), filter);
}
@Override
public void apply(int inputTexId, int width, int height, int outputTexId) {
beginGLEffect();
Frame inputFrame = frameFromTexture(inputTexId, width, height);
Frame outputFrame = frameFromTexture(outputTexId, width, height);
Frame resultFrame = mFunction.executeWithArgList(mInputName, inputFrame);
outputFrame.setDataFromFrame(resultFrame);
inputFrame.release();
outputFrame.release();
resultFrame.release();
endGLEffect();
}
@Override
public void setParameter(String parameterKey, Object value) {
mFunction.setInputValue(parameterKey, value);
}
@Override
public void release() {
mFunction.tearDown();
mFunction = null;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.media.effect;
import android.filterfw.core.Filter;
import android.filterfw.core.FilterFactory;
import android.filterfw.core.FilterFunction;
import android.filterfw.core.Frame;
import android.media.effect.Effect;
import android.media.effect.EffectContext;
import android.util.Log;
/**
* Effect subclass for effects based on a single Filter with output size differnet
* from input. Subclasses need only invoke the constructor with the correct arguments
* to obtain an Effect implementation.
*
* @hide
*/
public class SizeChangeEffect extends SingleFilterEffect {
public SizeChangeEffect(EffectContext context,
String name,
Class filterClass,
String inputName,
String outputName,
Object... finalParameters) {
super(context, name, filterClass, inputName, outputName, finalParameters);
}
@Override
public void apply(int inputTexId, int width, int height, int outputTexId) {
beginGLEffect();
Frame inputFrame = frameFromTexture(inputTexId, width, height);
Frame resultFrame = mFunction.executeWithArgList(mInputName, inputFrame);
int outputWidth = resultFrame.getFormat().getWidth();
int outputHeight = resultFrame.getFormat().getHeight();
Frame outputFrame = frameFromTexture(outputTexId, outputWidth, outputHeight);
outputFrame.setDataFromFrame(resultFrame);
inputFrame.release();
outputFrame.release();
resultFrame.release();
endGLEffect();
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.AutoFixFilter;
/**
* @hide
*/
public class AutoFixEffect extends SingleFilterEffect {
public AutoFixEffect(EffectContext context, String name) {
super(context, name, AutoFixFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.media.effect.effects;
import android.filterfw.core.Filter;
import android.filterfw.core.OneShotScheduler;
import android.media.effect.EffectContext;
import android.media.effect.FilterGraphEffect;
import android.media.effect.EffectUpdateListener;
import android.filterpacks.videoproc.BackDropperFilter;
import android.filterpacks.videoproc.BackDropperFilter.LearningDoneListener;
/**
* Background replacement Effect.
*
* Replaces the background of the input video stream with a selected video
* Learns the background when it first starts up;
* needs unobstructed view of background when this happens.
*
* Effect parameters:
* source: A URI for the background video
* Listener: Called when learning period is complete
*
* @hide
*/
public class BackDropperEffect extends FilterGraphEffect {
private static final String mGraphDefinition =
"@import android.filterpacks.base;\n" +
"@import android.filterpacks.videoproc;\n" +
"@import android.filterpacks.videosrc;\n" +
"\n" +
"@filter GLTextureSource foreground {\n" +
" texId = 0;\n" + // Will be set by base class
" width = 0;\n" +
" height = 0;\n" +
" repeatFrame = true;\n" +
"}\n" +
"\n" +
"@filter MediaSource background {\n" +
" sourceUrl = \"no_file_specified\";\n" +
" waitForNewFrame = false;\n" +
" sourceIsUrl = true;\n" +
"}\n" +
"\n" +
"@filter BackDropperFilter replacer {\n" +
" autowbToggle = 1;\n" +
"}\n" +
"\n" +
"@filter GLTextureTarget output {\n" +
" texId = 0;\n" +
"}\n" +
"\n" +
"@connect foreground[frame] => replacer[video];\n" +
"@connect background[video] => replacer[background];\n" +
"@connect replacer[video] => output[frame];\n";
private EffectUpdateListener mEffectListener = null;
private LearningDoneListener mLearningListener = new LearningDoneListener() {
public void onLearningDone(BackDropperFilter filter) {
if (mEffectListener != null) {
mEffectListener.onEffectUpdated(BackDropperEffect.this, null);
}
}
};
public BackDropperEffect(EffectContext context, String name) {
super(context, name, mGraphDefinition, "foreground", "output", OneShotScheduler.class);
Filter replacer = mGraph.getFilter("replacer");
replacer.setInputValue("learningDoneListener", mLearningListener);
}
@Override
public void setParameter(String parameterKey, Object value) {
if (parameterKey.equals("source")) {
Filter background = mGraph.getFilter("background");
background.setInputValue("sourceUrl", value);
}
}
@Override
public void setUpdateListener(EffectUpdateListener listener) {
mEffectListener = listener;
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.BitmapOverlayFilter;
/**
* @hide
*/
public class BitmapOverlayEffect extends SingleFilterEffect {
public BitmapOverlayEffect(EffectContext context, String name) {
super(context, name, BitmapOverlayFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.BlackWhiteFilter;
/**
* @hide
*/
public class BlackWhiteEffect extends SingleFilterEffect {
public BlackWhiteEffect(EffectContext context, String name) {
super(context, name, BlackWhiteFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.BrightnessFilter;
/**
* @hide
*/
public class BrightnessEffect extends SingleFilterEffect {
public BrightnessEffect(EffectContext context, String name) {
super(context, name, BrightnessFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.ColorTemperatureFilter;
/**
* @hide
*/
public class ColorTemperatureEffect extends SingleFilterEffect {
public ColorTemperatureEffect(EffectContext context, String name) {
super(context, name, ColorTemperatureFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.ContrastFilter;
/**
* @hide
*/
public class ContrastEffect extends SingleFilterEffect {
public ContrastEffect(EffectContext context, String name) {
super(context, name, ContrastFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SizeChangeEffect;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.CropRectFilter;
/**
* @hide
*/
//public class CropEffect extends SingleFilterEffect {
public class CropEffect extends SizeChangeEffect {
public CropEffect(EffectContext context, String name) {
super(context, name, CropRectFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.CrossProcessFilter;
/**
* @hide
*/
public class CrossProcessEffect extends SingleFilterEffect {
public CrossProcessEffect(EffectContext context, String name) {
super(context, name, CrossProcessFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.DocumentaryFilter;
/**
* @hide
*/
public class DocumentaryEffect extends SingleFilterEffect {
public DocumentaryEffect(EffectContext context, String name) {
super(context, name, DocumentaryFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.DuotoneFilter;
/**
* @hide
*/
public class DuotoneEffect extends SingleFilterEffect {
public DuotoneEffect(EffectContext context, String name) {
super(context, name, DuotoneFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.FillLightFilter;
/**
* @hide
*/
public class FillLightEffect extends SingleFilterEffect {
public FillLightEffect(EffectContext context, String name) {
super(context, name, FillLightFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.FisheyeFilter;
/**
* @hide
*/
public class FisheyeEffect extends SingleFilterEffect {
public FisheyeEffect(EffectContext context, String name) {
super(context, name, FisheyeFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.FlipFilter;
/**
* @hide
*/
public class FlipEffect extends SingleFilterEffect {
public FlipEffect(EffectContext context, String name) {
super(context, name, FlipFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.GrainFilter;
/**
* @hide
*/
public class GrainEffect extends SingleFilterEffect {
public GrainEffect(EffectContext context, String name) {
super(context, name, GrainFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.ToGrayFilter;
/**
* @hide
*/
public class GrayscaleEffect extends SingleFilterEffect {
public GrayscaleEffect(EffectContext context, String name) {
super(context, name, ToGrayFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.media.effect.effects;
import android.filterfw.core.Frame;
import android.media.effect.EffectContext;
import android.media.effect.FilterEffect;
/**
* @hide
*/
public class IdentityEffect extends FilterEffect {
public IdentityEffect(EffectContext context, String name) {
super(context, name);
}
@Override
public void apply(int inputTexId, int width, int height, int outputTexId) {
beginGLEffect();
Frame inputFrame = frameFromTexture(inputTexId, width, height);
Frame outputFrame = frameFromTexture(outputTexId, width, height);
outputFrame.setDataFromFrame(inputFrame);
inputFrame.release();
outputFrame.release();
endGLEffect();
}
@Override
public void setParameter(String parameterKey, Object value) {
throw new IllegalArgumentException("Unknown parameter " + parameterKey
+ " for IdentityEffect!");
}
@Override
public void release() {
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.LomoishFilter;
/**
* @hide
*/
public class LomoishEffect extends SingleFilterEffect {
public LomoishEffect(EffectContext context, String name) {
super(context, name, LomoishFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.NegativeFilter;
/**
* @hide
*/
public class NegativeEffect extends SingleFilterEffect {
public NegativeEffect(EffectContext context, String name) {
super(context, name, NegativeFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.PosterizeFilter;
/**
* @hide
*/
public class PosterizeEffect extends SingleFilterEffect {
public PosterizeEffect(EffectContext context, String name) {
super(context, name, PosterizeFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.RedEyeFilter;
/**
* @hide
*/
public class RedEyeEffect extends SingleFilterEffect {
public RedEyeEffect(EffectContext context, String name) {
super(context, name, RedEyeFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SizeChangeEffect;
import android.filterpacks.imageproc.RotateFilter;
/**
* @hide
*/
public class RotateEffect extends SizeChangeEffect {
public RotateEffect(EffectContext context, String name) {
super(context, name, RotateFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.SaturateFilter;
/**
* @hide
*/
public class SaturateEffect extends SingleFilterEffect {
public SaturateEffect(EffectContext context, String name) {
super(context, name, SaturateFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.SepiaFilter;
/**
* @hide
*/
public class SepiaEffect extends SingleFilterEffect {
public SepiaEffect(EffectContext context, String name) {
super(context, name, SepiaFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.SharpenFilter;
/**
* @hide
*/
public class SharpenEffect extends SingleFilterEffect {
public SharpenEffect(EffectContext context, String name) {
super(context, name, SharpenFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.StraightenFilter;
/**
* @hide
*/
public class StraightenEffect extends SingleFilterEffect {
public StraightenEffect(EffectContext context, String name) {
super(context, name, StraightenFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.TintFilter;
/**
* @hide
*/
public class TintEffect extends SingleFilterEffect {
public TintEffect(EffectContext context, String name) {
super(context, name, TintFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.media.effect.effects;
import android.media.effect.EffectContext;
import android.media.effect.SingleFilterEffect;
import android.filterpacks.imageproc.VignetteFilter;
/**
* @hide
*/
public class VignetteEffect extends SingleFilterEffect {
public VignetteEffect(EffectContext context, String name) {
super(context, name, VignetteFilter.class, "image", "image");
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.media.effect;
/**
* <h1>Effect Framework</h1>
*
* This package includes a collection of high-performance visual effects that make use of the
* mobile filter framework subsystem.
*
* TODO: More Documentation
*
*/

View File

@@ -0,0 +1,54 @@
<HTML>
<BODY>
<p>Provides classes that allow you to apply a variety of visual effects to images and
videos. For example, you can easily fix red-eye, convert an image to grayscale,
adjust brightness, adjust saturation, rotate an image, apply a fisheye effect, and much more. The
system performs all effects processing on the GPU to obtain maximum performance.</p>
<p>For maximum performance, effects are applied directly to OpenGL textures, so your application
must have a valid OpenGL context before it can use the effects APIs. The textures to which you apply
effects may be from bitmaps, videos or even the camera. However, there are certain restrictions that
textures must meet:</p>
<ol>
<li>They must be bound to a {@link android.opengl.GLES20#GL_TEXTURE_2D} texture image</li>
<li>They must contain at least one mipmap level</li>
</ol>
<p>An {@link android.media.effect.Effect} object defines a single media effect that you can apply to
an image frame. The basic workflow to create an {@link android.media.effect.Effect} is:</p>
<ol>
<li>Call {@link android.media.effect.EffectContext#createWithCurrentGlContext
EffectContext.createWithCurrentGlContext()} from your OpenGL ES 2.0 context.</li>
<li>Use the returned {@link android.media.effect.EffectContext} to call {@link
android.media.effect.EffectContext#getFactory EffectContext.getFactory()}, which returns an instance
of {@link android.media.effect.EffectFactory}.</li>
<li>Call {@link android.media.effect.EffectFactory#createEffect createEffect()}, passing it an
effect name from @link android.media.effect.EffectFactory}, such as {@link
android.media.effect.EffectFactory#EFFECT_FISHEYE} or {@link
android.media.effect.EffectFactory#EFFECT_VIGNETTE}.</li>
</ol>
<p>You can adjust an effects parameters by calling {@link android.media.effect.Effect#setParameter
setParameter()} and passing a parameter name and parameter value. Each type of effect accepts
different parameters, which are documented with the effect name. For example, {@link
android.media.effect.EffectFactory#EFFECT_FISHEYE} has one parameter for the {@code scale} of the
distortion.</p>
<p>To apply an effect on a texture, call {@link android.media.effect.Effect#apply apply()} on the
{@link
android.media.effect.Effect} and pass in the input texture, its width and height, and the output
texture. The input texture must be bound to a {@link android.opengl.GLES20#GL_TEXTURE_2D} texture
image (usually done by calling the {@link android.opengl.GLES20#glTexImage2D glTexImage2D()}
function). You may provide multiple mipmap levels. If the output texture has not been bound to a
texture image, it will be automatically bound by the effect as a {@link
android.opengl.GLES20#GL_TEXTURE_2D} and with one mipmap level (0), which will have the same
size as the input.</p>
<p class="note"><strong>Note:</strong> All effects listed in {@link
android.media.effect.EffectFactory} are guaranteed to be supported. However, some additional effects
available from external libraries are not supported by all devices, so you must first check if the
desired effect from the external library is supported by calling {@link
android.media.effect.EffectFactory#isEffectSupported isEffectSupported()}.</p>
</BODY>
</HTML>

View File

@@ -0,0 +1,53 @@
# 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.
#
#####################
# Build native sublibraries
include $(all-subdir-makefiles)
#####################
# Build main libfilterfw
include $(CLEAR_VARS)
LOCAL_MODULE := libfilterfw
LOCAL_MODULE_TAGS := optional
LOCAL_WHOLE_STATIC_LIBRARIES := libfilterfw_jni \
libfilterfw_native
LOCAL_SHARED_LIBRARIES := libstlport \
libGLESv2 \
libEGL \
libgui \
libdl \
libcutils \
libutils \
libandroid \
libjnigraphics \
libmedia \
libmedia_native
# Don't prelink this library. For more efficient code, you may want
# to add this library to the prelink map and set this to true. However,
# it's difficult to do this for applications that are not supplied as
# part of a system image.
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1,99 @@
/*
* 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;
import android.filterfw.core.Filter;
import android.filterfw.core.FilterFactory;
import android.filterfw.core.FilterFunction;
import android.filterfw.core.Frame;
import android.filterfw.core.FrameManager;
/**
* A FilterFunctionEnvironment provides a simple functional front-end to manually executing
* filters. Use this environment if a graph-based approach is not convenient for your case.
* Typically, a FilterFunctionEnvironment is used as follows:
* 1. Instantiate a new FilterFunctionEnvironment instance.
* 2. Perform any configuration, such as setting a GL environment.
* 3. Wrap Filters into FilterFunctions by calling createFunction().
* 4. Execute FilterFunctions individually and use the results for further processing.
* Additionally, there is a convenience method to execute a number of filters in sequence.
* @hide
*/
public class FilterFunctionEnvironment extends MffEnvironment {
/**
* Create a new FilterFunctionEnvironment with the default components.
*/
public FilterFunctionEnvironment() {
super(null);
}
/**
* Create a new FilterFunctionEnvironment with a custom FrameManager. Pass null to auto-create
* a FrameManager.
*
* @param frameManager The FrameManager to use, or null to auto-create one.
*/
public FilterFunctionEnvironment(FrameManager frameManager) {
super(frameManager);
}
/**
* Create a new FilterFunction from a specific filter class. The function is initialized with
* the given key-value list of parameters. Note, that this function uses the default shared
* FilterFactory to create the filter instance.
*
* @param filterClass The class of the filter to wrap. This must be a Filter subclass.
* @param parameters An argument list of alternating key-value filter parameters.
* @return A new FilterFunction instance.
*/
public FilterFunction createFunction(Class filterClass, Object... parameters) {
String filterName = "FilterFunction(" + filterClass.getSimpleName() + ")";
Filter filter = FilterFactory.sharedFactory().createFilterByClass(filterClass, filterName);
filter.initWithAssignmentList(parameters);
return new FilterFunction(getContext(), filter);
}
/**
* Convenience method to execute a sequence of filter functions. Note that every function in
* the list MUST have one input and one output port, except the first filter (which must not
* have any input ports) and the last filter (which may not have any output ports).
*
* @param functions A list of filter functions. The first filter must be a source filter.
* @return The result of the last filter executed, or null if the last filter did not
produce any output.
*
public Frame executeSequence(FilterFunction[] functions) {
Frame oldFrame = null;
Frame newFrame = null;
for (FilterFunction filterFunction : functions) {
if (oldFrame == null) {
newFrame = filterFunction.executeWithArgList();
} else {
newFrame = filterFunction.executeWithArgList(oldFrame);
oldFrame.release();
}
oldFrame = newFrame;
}
if (oldFrame != null) {
oldFrame.release();
}
return newFrame;
}*/
}

View File

@@ -0,0 +1,197 @@
/*
* 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;
import android.content.Context;
import android.filterfw.core.AsyncRunner;
import android.filterfw.core.FilterGraph;
import android.filterfw.core.FilterContext;
import android.filterfw.core.FrameManager;
import android.filterfw.core.GraphRunner;
import android.filterfw.core.RoundRobinScheduler;
import android.filterfw.core.SyncRunner;
import android.filterfw.io.GraphIOException;
import android.filterfw.io.GraphReader;
import android.filterfw.io.TextGraphReader;
import java.util.ArrayList;
/**
* A GraphEnvironment provides a simple front-end to filter graph setup and execution using the
* mobile filter framework. Typically, you use a GraphEnvironment in the following fashion:
* 1. Instantiate a new GraphEnvironment instance.
* 2. Perform any configuration, such as adding graph references and setting a GL environment.
* 3. Load a graph file using loadGraph() or add a graph using addGraph().
* 4. Obtain a GraphRunner instance using getRunner().
* 5. Execute the obtained runner.
* Note that it is possible to add multiple graphs and runners to a single GraphEnvironment.
*
* @hide
*/
public class GraphEnvironment extends MffEnvironment {
public static final int MODE_ASYNCHRONOUS = 1;
public static final int MODE_SYNCHRONOUS = 2;
private GraphReader mGraphReader;
private ArrayList<GraphHandle> mGraphs = new ArrayList<GraphHandle>();
private class GraphHandle {
private FilterGraph mGraph;
private AsyncRunner mAsyncRunner;
private SyncRunner mSyncRunner;
public GraphHandle(FilterGraph graph) {
mGraph = graph;
}
public FilterGraph getGraph() {
return mGraph;
}
public AsyncRunner getAsyncRunner(FilterContext environment) {
if (mAsyncRunner == null) {
mAsyncRunner = new AsyncRunner(environment, RoundRobinScheduler.class);
mAsyncRunner.setGraph(mGraph);
}
return mAsyncRunner;
}
public GraphRunner getSyncRunner(FilterContext environment) {
if (mSyncRunner == null) {
mSyncRunner = new SyncRunner(environment, mGraph, RoundRobinScheduler.class);
}
return mSyncRunner;
}
}
/**
* Create a new GraphEnvironment with default components.
*/
public GraphEnvironment() {
super(null);
}
/**
* Create a new GraphEnvironment with a custom FrameManager and GraphReader. Specifying null
* for either of these, will auto-create a default instance.
*
* @param frameManager The FrameManager to use, or null to auto-create one.
* @param reader The GraphReader to use for graph loading, or null to auto-create one.
* Note, that the reader will not be created until it is required. Pass
* null if you will not load any graph files.
*/
public GraphEnvironment(FrameManager frameManager, GraphReader reader) {
super(frameManager);
mGraphReader = reader;
}
/**
* Returns the used graph reader. This will create one, if a reader has not been set already.
*/
public GraphReader getGraphReader() {
if (mGraphReader == null) {
mGraphReader = new TextGraphReader();
}
return mGraphReader;
}
/**
* Add graph references to resolve during graph reading. The references added here are shared
* among all graphs.
*
* @param references An alternating argument list of keys (Strings) and values.
*/
public void addReferences(Object... references) {
getGraphReader().addReferencesByKeysAndValues(references);
}
/**
* Loads a graph file from the specified resource and adds it to this environment.
*
* @param context The context in which to read the resource.
* @param resourceId The ID of the graph resource to load.
* @return A unique ID for the graph.
*/
public int loadGraph(Context context, int resourceId) {
// Read the file into a graph
FilterGraph graph = null;
try {
graph = getGraphReader().readGraphResource(context, resourceId);
} catch (GraphIOException e) {
throw new RuntimeException("Could not read graph: " + e.getMessage());
}
// Add graph to our list of graphs
return addGraph(graph);
}
/**
* Add a graph to the environment. Consider using loadGraph() if you are loading a graph from
* a graph file.
*
* @param graph The graph to add to the environment.
* @return A unique ID for the added graph.
*/
public int addGraph(FilterGraph graph) {
GraphHandle graphHandle = new GraphHandle(graph);
mGraphs.add(graphHandle);
return mGraphs.size() - 1;
}
/**
* Access a specific graph of this environment given a graph ID (previously returned from
* loadGraph() or addGraph()). Throws an InvalidArgumentException if no graph with the
* specified ID could be found.
*
* @param graphId The ID of the graph to get.
* @return The graph with the specified ID.
*/
public FilterGraph getGraph(int graphId) {
if (graphId < 0 || graphId >= mGraphs.size()) {
throw new IllegalArgumentException(
"Invalid graph ID " + graphId + " specified in runGraph()!");
}
return mGraphs.get(graphId).getGraph();
}
/**
* Get a GraphRunner instance for the graph with the specified ID. The GraphRunner instance can
* be used to execute the graph. Throws an InvalidArgumentException if no graph with the
* specified ID could be found.
*
* @param graphId The ID of the graph to get.
* @param executionMode The mode of graph execution. Currently this can be either
MODE_SYNCHRONOUS or MODE_ASYNCHRONOUS.
* @return A GraphRunner instance for this graph.
*/
public GraphRunner getRunner(int graphId, int executionMode) {
switch (executionMode) {
case MODE_ASYNCHRONOUS:
return mGraphs.get(graphId).getAsyncRunner(getContext());
case MODE_SYNCHRONOUS:
return mGraphs.get(graphId).getSyncRunner(getContext());
default:
throw new RuntimeException(
"Invalid execution mode " + executionMode + " specified in getRunner()!");
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* 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;
import android.filterfw.core.CachedFrameManager;
import android.filterfw.core.FilterContext;
import android.filterfw.core.FrameManager;
import android.filterfw.core.GLEnvironment;
/**
* Base class for mobile filter framework (MFF) frontend environments. These convenience classes
* allow using the filter framework without the requirement of performing manual setup of its
* required components.
*
* @hide
*/
public class MffEnvironment {
private FilterContext mContext;
/**
* Protected constructor to initialize the environment's essential components. These are the
* frame-manager and the filter-context. Passing in null for the frame-manager causes this
* to be auto-created.
*
* @param frameManager The FrameManager to use or null to auto-create one.
*/
protected MffEnvironment(FrameManager frameManager) {
// Get or create the frame manager
if (frameManager == null) {
frameManager = new CachedFrameManager();
}
// Setup the environment
mContext = new FilterContext();
mContext.setFrameManager(frameManager);
}
/**
* Returns the environment's filter-context.
*/
public FilterContext getContext() {
return mContext;
}
/**
* Set the environment's GL environment to the specified environment. This does not activate
* the environment.
*/
public void setGLEnvironment(GLEnvironment glEnvironment) {
mContext.initGLEnvironment(glEnvironment);
}
/**
* Create and activate a new GL environment for use in this filter context.
*/
public void createGLEnvironment() {
GLEnvironment glEnvironment = new GLEnvironment();
glEnvironment.initWithNewContext();
setGLEnvironment(glEnvironment);
}
/**
* Activate the GL environment for use in the current thread. A GL environment must have been
* previously set or created using setGLEnvironment() or createGLEnvironment()! Call this after
* having switched to a new thread for GL filter execution.
*/
public void activateGLEnvironment() {
GLEnvironment glEnv = mContext.getGLEnvironment();
if (glEnv != null) {
mContext.getGLEnvironment().activate();
} else {
throw new NullPointerException("No GLEnvironment in place to activate!");
}
}
/**
* Deactivate the GL environment from use in the current thread. A GL environment must have been
* previously set or created using setGLEnvironment() or createGLEnvironment()! Call this before
* running GL filters in another thread.
*/
public void deactivateGLEnvironment() {
GLEnvironment glEnv = mContext.getGLEnvironment();
if (glEnv != null) {
mContext.getGLEnvironment().deactivate();
} else {
throw new NullPointerException("No GLEnvironment in place to deactivate!");
}
}
}

View File

@@ -0,0 +1,247 @@
/*
* 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 android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;
import java.lang.InterruptedException;
import java.lang.Runnable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
/**
* @hide
*/
public class AsyncRunner extends GraphRunner{
private Class mSchedulerClass;
private SyncRunner mRunner;
private AsyncRunnerTask mRunTask;
private OnRunnerDoneListener mDoneListener;
private boolean isProcessing;
private Exception mException;
private class RunnerResult {
public int status = RESULT_UNKNOWN;
public Exception exception;
}
private class AsyncRunnerTask extends AsyncTask<SyncRunner, Void, RunnerResult> {
private static final String TAG = "AsyncRunnerTask";
@Override
protected RunnerResult doInBackground(SyncRunner... runner) {
RunnerResult result = new RunnerResult();
try {
if (runner.length > 1) {
throw new RuntimeException("More than one runner received!");
}
runner[0].assertReadyToStep();
// Preparation
if (mLogVerbose) Log.v(TAG, "Starting background graph processing.");
activateGlContext();
if (mLogVerbose) Log.v(TAG, "Preparing filter graph for processing.");
runner[0].beginProcessing();
if (mLogVerbose) Log.v(TAG, "Running graph.");
// Run loop
result.status = RESULT_RUNNING;
while (!isCancelled() && result.status == RESULT_RUNNING) {
if (!runner[0].performStep()) {
result.status = runner[0].determinePostRunState();
if (result.status == GraphRunner.RESULT_SLEEPING) {
runner[0].waitUntilWake();
result.status = RESULT_RUNNING;
}
}
}
// Cleanup
if (isCancelled()) {
result.status = RESULT_STOPPED;
}
} catch (Exception exception) {
result.exception = exception;
result.status = RESULT_ERROR;
}
// Deactivate context.
try {
deactivateGlContext();
} catch (Exception exception) {
result.exception = exception;
result.status = RESULT_ERROR;
}
if (mLogVerbose) Log.v(TAG, "Done with background graph processing.");
return result;
}
@Override
protected void onCancelled(RunnerResult result) {
onPostExecute(result);
}
@Override
protected void onPostExecute(RunnerResult result) {
if (mLogVerbose) Log.v(TAG, "Starting post-execute.");
setRunning(false);
if (result == null) {
// Cancelled before got to doInBackground
result = new RunnerResult();
result.status = RESULT_STOPPED;
}
setException(result.exception);
if (result.status == RESULT_STOPPED || result.status == RESULT_ERROR) {
if (mLogVerbose) Log.v(TAG, "Closing filters.");
try {
mRunner.close();
} catch (Exception exception) {
result.status = RESULT_ERROR;
setException(exception);
}
}
if (mDoneListener != null) {
if (mLogVerbose) Log.v(TAG, "Calling graph done callback.");
mDoneListener.onRunnerDone(result.status);
}
if (mLogVerbose) Log.v(TAG, "Completed post-execute.");
}
}
private boolean mLogVerbose;
private static final String TAG = "AsyncRunner";
/** Create a new asynchronous graph runner with the given filter
* context, and the given scheduler class.
*
* Must be created on the UI thread.
*/
public AsyncRunner(FilterContext context, Class schedulerClass) {
super(context);
mSchedulerClass = schedulerClass;
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
}
/** Create a new asynchronous graph runner with the given filter
* context. Uses a default scheduler.
*
* Must be created on the UI thread.
*/
public AsyncRunner(FilterContext context) {
super(context);
mSchedulerClass = SimpleScheduler.class;
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
}
/** Set a callback to be called in the UI thread once the AsyncRunner
* completes running a graph, whether the completion is due to a stop() call
* or the filters running out of data to process.
*/
@Override
public void setDoneCallback(OnRunnerDoneListener listener) {
mDoneListener = listener;
}
/** Sets the graph to be run. Will call prepare() on graph. Cannot be called
* when a graph is already running.
*/
synchronized public void setGraph(FilterGraph graph) {
if (isRunning()) {
throw new RuntimeException("Graph is already running!");
}
mRunner = new SyncRunner(mFilterContext, graph, mSchedulerClass);
}
@Override
public FilterGraph getGraph() {
return mRunner != null ? mRunner.getGraph() : null;
}
/** Execute the graph in a background thread. */
@Override
synchronized public void run() {
if (mLogVerbose) Log.v(TAG, "Running graph.");
setException(null);
if (isRunning()) {
throw new RuntimeException("Graph is already running!");
}
if (mRunner == null) {
throw new RuntimeException("Cannot run before a graph is set!");
}
mRunTask = this.new AsyncRunnerTask();
setRunning(true);
mRunTask.execute(mRunner);
}
/** Stop graph execution. This is an asynchronous call; register a callback
* with setDoneCallback to be notified of when the background processing has
* been completed. Calling stop will close the filter graph. */
@Override
synchronized public void stop() {
if (mRunTask != null && !mRunTask.isCancelled() ) {
if (mLogVerbose) Log.v(TAG, "Stopping graph.");
mRunTask.cancel(false);
}
}
@Override
synchronized public void close() {
if (isRunning()) {
throw new RuntimeException("Cannot close graph while it is running!");
}
if (mLogVerbose) Log.v(TAG, "Closing filters.");
mRunner.close();
}
/** Check if background processing is happening */
@Override
synchronized public boolean isRunning() {
return isProcessing;
}
@Override
synchronized public Exception getError() {
return mException;
}
synchronized private void setRunning(boolean running) {
isProcessing = running;
}
synchronized private void setException(Exception exception) {
mException = exception;
}
}

View File

@@ -0,0 +1,155 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.SimpleFrameManager;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @hide
*/
public class CachedFrameManager extends SimpleFrameManager {
private SortedMap<Integer, Frame> mAvailableFrames;
private int mStorageCapacity = 24 * 1024 * 1024; // Cap default storage to 24MB
private int mStorageSize = 0;
private int mTimeStamp = 0;
public CachedFrameManager() {
super();
mAvailableFrames = new TreeMap<Integer, Frame>();
}
@Override
public Frame newFrame(FrameFormat format) {
Frame result = findAvailableFrame(format, Frame.NO_BINDING, 0);
if (result == null) {
result = super.newFrame(format);
}
result.setTimestamp(Frame.TIMESTAMP_NOT_SET);
return result;
}
@Override
public Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId) {
Frame result = findAvailableFrame(format, bindingType, bindingId);
if (result == null) {
result = super.newBoundFrame(format, bindingType, bindingId);
}
result.setTimestamp(Frame.TIMESTAMP_NOT_SET);
return result;
}
@Override
public Frame retainFrame(Frame frame) {
return super.retainFrame(frame);
}
@Override
public Frame releaseFrame(Frame frame) {
if (frame.isReusable()) {
int refCount = frame.decRefCount();
if (refCount == 0 && frame.hasNativeAllocation()) {
if (!storeFrame(frame)) {
frame.releaseNativeAllocation();
}
return null;
} else if (refCount < 0) {
throw new RuntimeException("Frame reference count dropped below 0!");
}
} else {
super.releaseFrame(frame);
}
return frame;
}
public void clearCache() {
for (Frame frame : mAvailableFrames.values()) {
frame.releaseNativeAllocation();
}
mAvailableFrames.clear();
}
@Override
public void tearDown() {
clearCache();
}
private boolean storeFrame(Frame frame) {
synchronized(mAvailableFrames) {
// Make sure this frame alone does not exceed capacity
int frameSize = frame.getFormat().getSize();
if (frameSize > mStorageCapacity) {
return false;
}
// Drop frames if adding this frame would exceed capacity
int newStorageSize = mStorageSize + frameSize;
while (newStorageSize > mStorageCapacity) {
dropOldestFrame();
newStorageSize = mStorageSize + frameSize;
}
// Store new frame
frame.onFrameStore();
mStorageSize = newStorageSize;
mAvailableFrames.put(mTimeStamp, frame);
++mTimeStamp;
return true;
}
}
private void dropOldestFrame() {
int oldest = mAvailableFrames.firstKey();
Frame frame = mAvailableFrames.get(oldest);
mStorageSize -= frame.getFormat().getSize();
frame.releaseNativeAllocation();
mAvailableFrames.remove(oldest);
}
private Frame findAvailableFrame(FrameFormat format, int bindingType, long bindingId) {
// Look for a frame that is compatible with the requested format
synchronized(mAvailableFrames) {
for (Map.Entry<Integer, Frame> entry : mAvailableFrames.entrySet()) {
Frame frame = entry.getValue();
// Check that format is compatible
if (frame.getFormat().isReplaceableBy(format)) {
// Check that binding is compatible (if frame is bound)
if ((bindingType == frame.getBindingType())
&& (bindingType == Frame.NO_BINDING || bindingId == frame.getBindingId())) {
// We found one! Take it out of the set of available frames and attach the
// requested format to it.
super.retainFrame(frame);
mAvailableFrames.remove(entry.getKey());
frame.onFrameFetch();
frame.reset(format);
mStorageSize -= format.getSize();
return frame;
}
}
}
}
return null;
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.lang.reflect.Field;
/**
* @hide
*/
public class FieldPort extends InputPort {
protected Field mField;
protected boolean mHasFrame;
protected boolean mValueWaiting = false;
protected Object mValue;
public FieldPort(Filter filter, String name, Field field, boolean hasDefault) {
super(filter, name);
mField = field;
mHasFrame = hasDefault;
}
@Override
public void clear() {
}
@Override
public void pushFrame(Frame frame) {
setFieldFrame(frame, false);
}
@Override
public void setFrame(Frame frame) {
setFieldFrame(frame, true);
}
@Override
public Object getTarget() {
try {
return mField.get(mFilter);
} catch (IllegalAccessException e) {
return null;
}
}
@Override
public synchronized void transfer(FilterContext context) {
if (mValueWaiting) {
try {
mField.set(mFilter, mValue);
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Access to field '" + mField.getName() + "' was denied!");
}
mValueWaiting = false;
if (context != null) {
mFilter.notifyFieldPortValueUpdated(mName, context);
}
}
}
@Override
public synchronized Frame pullFrame() {
throw new RuntimeException("Cannot pull frame on " + this + "!");
}
@Override
public synchronized boolean hasFrame() {
return mHasFrame;
}
@Override
public synchronized boolean acceptsFrame() {
return !mValueWaiting;
}
@Override
public String toString() {
return "field " + super.toString();
}
protected synchronized void setFieldFrame(Frame frame, boolean isAssignment) {
assertPortIsOpen();
checkFrameType(frame, isAssignment);
// Store the object value
Object value = frame.getObjectValue();
if ((value == null && mValue != null) || !value.equals(mValue)) {
mValue = value;
mValueWaiting = true;
}
// Since a frame was set, mark this port as having a frame to pull
mHasFrame = true;
}
}

View File

@@ -0,0 +1,709 @@
/*
* 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 android.filterfw.core.FilterContext;
import android.filterfw.core.FilterPort;
import android.filterfw.core.KeyValueMap;
import android.filterfw.io.TextGraphReader;
import android.filterfw.io.GraphIOException;
import android.filterfw.format.ObjectFormat;
import android.util.Log;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.Thread;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.LinkedList;
import java.util.Set;
/**
* @hide
*/
public abstract class Filter {
static final int STATUS_PREINIT = 0;
static final int STATUS_UNPREPARED = 1;
static final int STATUS_PREPARED = 2;
static final int STATUS_PROCESSING = 3;
static final int STATUS_SLEEPING = 4;
static final int STATUS_FINISHED = 5;
static final int STATUS_ERROR = 6;
static final int STATUS_RELEASED = 7;
private String mName;
private int mInputCount = -1;
private int mOutputCount = -1;
private HashMap<String, InputPort> mInputPorts;
private HashMap<String, OutputPort> mOutputPorts;
private HashSet<Frame> mFramesToRelease;
private HashMap<String, Frame> mFramesToSet;
private int mStatus = 0;
private boolean mIsOpen = false;
private int mSleepDelay;
private long mCurrentTimestamp;
private boolean mLogVerbose;
private static final String TAG = "Filter";
public Filter(String name) {
mName = name;
mFramesToRelease = new HashSet<Frame>();
mFramesToSet = new HashMap<String, Frame>();
mStatus = STATUS_PREINIT;
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
}
/** Tests to see if a given filter is installed on the system. Requires
* full filter package name, including filterpack.
*/
public static final boolean isAvailable(String filterName) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Class filterClass;
// First see if a class of that name exists
try {
filterClass = contextClassLoader.loadClass(filterName);
} catch (ClassNotFoundException e) {
return false;
}
// Then make sure it's a subclass of Filter.
try {
filterClass.asSubclass(Filter.class);
} catch (ClassCastException e) {
return false;
}
return true;
}
public final void initWithValueMap(KeyValueMap valueMap) {
// Initialization
initFinalPorts(valueMap);
// Setup remaining ports
initRemainingPorts(valueMap);
// This indicates that final ports can no longer be set
mStatus = STATUS_UNPREPARED;
}
public final void initWithAssignmentString(String assignments) {
try {
KeyValueMap valueMap = new TextGraphReader().readKeyValueAssignments(assignments);
initWithValueMap(valueMap);
} catch (GraphIOException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
public final void initWithAssignmentList(Object... keyValues) {
KeyValueMap valueMap = new KeyValueMap();
valueMap.setKeyValues(keyValues);
initWithValueMap(valueMap);
}
public final void init() throws ProtocolException {
KeyValueMap valueMap = new KeyValueMap();
initWithValueMap(valueMap);
}
public String getFilterClassName() {
return getClass().getSimpleName();
}
public final String getName() {
return mName;
}
public boolean isOpen() {
return mIsOpen;
}
public void setInputFrame(String inputName, Frame frame) {
FilterPort port = getInputPort(inputName);
if (!port.isOpen()) {
port.open();
}
port.setFrame(frame);
}
public final void setInputValue(String inputName, Object value) {
setInputFrame(inputName, wrapInputValue(inputName, value));
}
protected void prepare(FilterContext context) {
}
protected void parametersUpdated(Set<String> updated) {
}
protected void delayNextProcess(int millisecs) {
mSleepDelay = millisecs;
mStatus = STATUS_SLEEPING;
}
public abstract void setupPorts();
public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
return null;
}
public final FrameFormat getInputFormat(String portName) {
InputPort inputPort = getInputPort(portName);
return inputPort.getSourceFormat();
}
public void open(FilterContext context) {
}
public abstract void process(FilterContext context);
public final int getSleepDelay() {
return 250;
}
public void close(FilterContext context) {
}
public void tearDown(FilterContext context) {
}
public final int getNumberOfConnectedInputs() {
int c = 0;
for (InputPort inputPort : mInputPorts.values()) {
if (inputPort.isConnected()) {
++c;
}
}
return c;
}
public final int getNumberOfConnectedOutputs() {
int c = 0;
for (OutputPort outputPort : mOutputPorts.values()) {
if (outputPort.isConnected()) {
++c;
}
}
return c;
}
public final int getNumberOfInputs() {
return mOutputPorts == null ? 0 : mInputPorts.size();
}
public final int getNumberOfOutputs() {
return mInputPorts == null ? 0 : mOutputPorts.size();
}
public final InputPort getInputPort(String portName) {
if (mInputPorts == null) {
throw new NullPointerException("Attempting to access input port '" + portName
+ "' of " + this + " before Filter has been initialized!");
}
InputPort result = mInputPorts.get(portName);
if (result == null) {
throw new IllegalArgumentException("Unknown input port '" + portName + "' on filter "
+ this + "!");
}
return result;
}
public final OutputPort getOutputPort(String portName) {
if (mInputPorts == null) {
throw new NullPointerException("Attempting to access output port '" + portName
+ "' of " + this + " before Filter has been initialized!");
}
OutputPort result = mOutputPorts.get(portName);
if (result == null) {
throw new IllegalArgumentException("Unknown output port '" + portName + "' on filter "
+ this + "!");
}
return result;
}
protected final void pushOutput(String name, Frame frame) {
if (frame.getTimestamp() == Frame.TIMESTAMP_NOT_SET) {
if (mLogVerbose) Log.v(TAG, "Default-setting output Frame timestamp on port " + name + " to " + mCurrentTimestamp);
frame.setTimestamp(mCurrentTimestamp);
}
getOutputPort(name).pushFrame(frame);
}
protected final Frame pullInput(String name) {
Frame result = getInputPort(name).pullFrame();
if (mCurrentTimestamp == Frame.TIMESTAMP_UNKNOWN) {
mCurrentTimestamp = result.getTimestamp();
if (mLogVerbose) Log.v(TAG, "Default-setting current timestamp from input port " + name + " to " + mCurrentTimestamp);
}
// As result is retained, we add it to the release pool here
mFramesToRelease.add(result);
return result;
}
public void fieldPortValueUpdated(String name, FilterContext context) {
}
/**
* Transfers any frame from an input port to its destination. This is useful to force a
* transfer from a FieldPort or ProgramPort to its connected target (field or program variable).
*/
protected void transferInputPortFrame(String name, FilterContext context) {
getInputPort(name).transfer(context);
}
/**
* Assigns all program variables to the ports they are connected to. Call this after
* constructing a Program instance with attached ProgramPorts.
*/
protected void initProgramInputs(Program program, FilterContext context) {
if (program != null) {
for (InputPort inputPort : mInputPorts.values()) {
if (inputPort.getTarget() == program) {
inputPort.transfer(context);
}
}
}
}
/**
* Adds an input port to the filter. You should call this from within setupPorts, if your
* filter has input ports. No type-checking is performed on the input. If you would like to
* check against a type mask, use
* {@link #addMaskedInputPort(String, FrameFormat) addMaskedInputPort} instead.
*
* @param name the name of the input port
*/
protected void addInputPort(String name) {
addMaskedInputPort(name, null);
}
/**
* Adds an input port to the filter. You should call this from within setupPorts, if your
* filter has input ports. When type-checking is performed, the input format is
* checked against the provided format mask. An exception is thrown in case of a conflict.
*
* @param name the name of the input port
* @param formatMask a format mask, which filters the allowable input types
*/
protected void addMaskedInputPort(String name, FrameFormat formatMask) {
InputPort port = new StreamPort(this, name);
if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + port);
mInputPorts.put(name, port);
port.setPortFormat(formatMask);
}
/**
* Adds an output port to the filter with a fixed output format. You should call this from
* within setupPorts, if your filter has output ports. You cannot use this method, if your
* output format depends on the input format (e.g. in a pass-through filter). In this case, use
* {@link #addOutputBasedOnInput(String, String) addOutputBasedOnInput} instead.
*
* @param name the name of the output port
* @param format the fixed output format of this port
*/
protected void addOutputPort(String name, FrameFormat format) {
OutputPort port = new OutputPort(this, name);
if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + port);
port.setPortFormat(format);
mOutputPorts.put(name, port);
}
/**
* Adds an output port to the filter. You should call this from within setupPorts, if your
* filter has output ports. Using this method indicates that the output format for this
* particular port, depends on the format of an input port. You MUST also override
* {@link #getOutputFormat(String, FrameFormat) getOutputFormat} to specify what format your
* filter will output for a given input. If the output format of your filter port does not
* depend on the input, use {@link #addOutputPort(String, FrameFormat) addOutputPort} instead.
*
* @param outputName the name of the output port
* @param inputName the name of the input port, that this output depends on
*/
protected void addOutputBasedOnInput(String outputName, String inputName) {
OutputPort port = new OutputPort(this, outputName);
if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + port);
port.setBasePort(getInputPort(inputName));
mOutputPorts.put(outputName, port);
}
protected void addFieldPort(String name,
Field field,
boolean hasDefault,
boolean isFinal) {
// Make sure field is accessible
field.setAccessible(true);
// Create port for this input
InputPort fieldPort = isFinal
? new FinalPort(this, name, field, hasDefault)
: new FieldPort(this, name, field, hasDefault);
// Create format for this input
if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + fieldPort);
MutableFrameFormat format = ObjectFormat.fromClass(field.getType(),
FrameFormat.TARGET_SIMPLE);
fieldPort.setPortFormat(format);
// Add port
mInputPorts.put(name, fieldPort);
}
protected void addProgramPort(String name,
String varName,
Field field,
Class varType,
boolean hasDefault) {
// Make sure field is accessible
field.setAccessible(true);
// Create port for this input
InputPort programPort = new ProgramPort(this, name, varName, field, hasDefault);
// Create format for this input
if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + programPort);
MutableFrameFormat format = ObjectFormat.fromClass(varType,
FrameFormat.TARGET_SIMPLE);
programPort.setPortFormat(format);
// Add port
mInputPorts.put(name, programPort);
}
protected void closeOutputPort(String name) {
getOutputPort(name).close();
}
/**
* Specifies whether the filter should not be scheduled until a frame is available on that
* input port. Note, that setting this to false, does not block a new frame from coming in
* (though there is no necessity to pull that frame for processing).
* @param portName the name of the input port.
* @param waits true, if the filter should wait for a frame on this port.
*/
protected void setWaitsOnInputPort(String portName, boolean waits) {
getInputPort(portName).setBlocking(waits);
}
/**
* Specifies whether the filter should not be scheduled until the output port is free, i.e.
* there is no frame waiting on that output.
* @param portName the name of the output port.
* @param waits true, if the filter should wait for the port to become free.
*/
protected void setWaitsOnOutputPort(String portName, boolean waits) {
getOutputPort(portName).setBlocking(waits);
}
public String toString() {
return "'" + getName() + "' (" + getFilterClassName() + ")";
}
// Core internal methods ///////////////////////////////////////////////////////////////////////
final Collection<InputPort> getInputPorts() {
return mInputPorts.values();
}
final Collection<OutputPort> getOutputPorts() {
return mOutputPorts.values();
}
final synchronized int getStatus() {
return mStatus;
}
final synchronized void unsetStatus(int flag) {
mStatus &= ~flag;
}
final synchronized void performOpen(FilterContext context) {
if (!mIsOpen) {
if (mStatus == STATUS_UNPREPARED) {
if (mLogVerbose) Log.v(TAG, "Preparing " + this);
prepare(context);
mStatus = STATUS_PREPARED;
}
if (mStatus == STATUS_PREPARED) {
if (mLogVerbose) Log.v(TAG, "Opening " + this);
open(context);
mStatus = STATUS_PROCESSING;
}
if (mStatus != STATUS_PROCESSING) {
throw new RuntimeException("Filter " + this + " was brought into invalid state during "
+ "opening (state: " + mStatus + ")!");
}
mIsOpen = true;
}
}
final synchronized void performProcess(FilterContext context) {
if (mStatus == STATUS_RELEASED) {
throw new RuntimeException("Filter " + this + " is already torn down!");
}
transferInputFrames(context);
if (mStatus < STATUS_PROCESSING) {
performOpen(context);
}
if (mLogVerbose) Log.v(TAG, "Processing " + this);
mCurrentTimestamp = Frame.TIMESTAMP_UNKNOWN;
process(context);
releasePulledFrames(context);
if (filterMustClose()) {
performClose(context);
}
}
final synchronized void performClose(FilterContext context) {
if (mIsOpen) {
if (mLogVerbose) Log.v(TAG, "Closing " + this);
mIsOpen = false;
mStatus = STATUS_PREPARED;
close(context);
closePorts();
}
}
final synchronized void performTearDown(FilterContext context) {
performClose(context);
if (mStatus != STATUS_RELEASED) {
tearDown(context);
mStatus = STATUS_RELEASED;
}
}
synchronized final boolean canProcess() {
if (mLogVerbose) Log.v(TAG, "Checking if can process: " + this + " (" + mStatus + ").");
if (mStatus <= STATUS_PROCESSING) {
return inputConditionsMet() && outputConditionsMet();
} else {
return false;
}
}
final void openOutputs() {
if (mLogVerbose) Log.v(TAG, "Opening all output ports on " + this + "!");
for (OutputPort outputPort : mOutputPorts.values()) {
if (!outputPort.isOpen()) {
outputPort.open();
}
}
}
final void clearInputs() {
for (InputPort inputPort : mInputPorts.values()) {
inputPort.clear();
}
}
final void clearOutputs() {
for (OutputPort outputPort : mOutputPorts.values()) {
outputPort.clear();
}
}
final void notifyFieldPortValueUpdated(String name, FilterContext context) {
if (mStatus == STATUS_PROCESSING || mStatus == STATUS_PREPARED) {
fieldPortValueUpdated(name, context);
}
}
final synchronized void pushInputFrame(String inputName, Frame frame) {
FilterPort port = getInputPort(inputName);
if (!port.isOpen()) {
port.open();
}
port.pushFrame(frame);
}
final synchronized void pushInputValue(String inputName, Object value) {
pushInputFrame(inputName, wrapInputValue(inputName, value));
}
// Filter internal methods /////////////////////////////////////////////////////////////////////
private final void initFinalPorts(KeyValueMap values) {
mInputPorts = new HashMap<String, InputPort>();
mOutputPorts = new HashMap<String, OutputPort>();
addAndSetFinalPorts(values);
}
private final void initRemainingPorts(KeyValueMap values) {
addAnnotatedPorts();
setupPorts(); // TODO: rename to addFilterPorts() ?
setInitialInputValues(values);
}
private final void addAndSetFinalPorts(KeyValueMap values) {
Class filterClass = getClass();
Annotation annotation;
for (Field field : filterClass.getDeclaredFields()) {
if ((annotation = field.getAnnotation(GenerateFinalPort.class)) != null) {
GenerateFinalPort generator = (GenerateFinalPort)annotation;
String name = generator.name().isEmpty() ? field.getName() : generator.name();
boolean hasDefault = generator.hasDefault();
addFieldPort(name, field, hasDefault, true);
if (values.containsKey(name)) {
setImmediateInputValue(name, values.get(name));
values.remove(name);
} else if (!generator.hasDefault()) {
throw new RuntimeException("No value specified for final input port '"
+ name + "' of filter " + this + "!");
}
}
}
}
private final void addAnnotatedPorts() {
Class filterClass = getClass();
Annotation annotation;
for (Field field : filterClass.getDeclaredFields()) {
if ((annotation = field.getAnnotation(GenerateFieldPort.class)) != null) {
GenerateFieldPort generator = (GenerateFieldPort)annotation;
addFieldGenerator(generator, field);
} else if ((annotation = field.getAnnotation(GenerateProgramPort.class)) != null) {
GenerateProgramPort generator = (GenerateProgramPort)annotation;
addProgramGenerator(generator, field);
} else if ((annotation = field.getAnnotation(GenerateProgramPorts.class)) != null) {
GenerateProgramPorts generators = (GenerateProgramPorts)annotation;
for (GenerateProgramPort generator : generators.value()) {
addProgramGenerator(generator, field);
}
}
}
}
private final void addFieldGenerator(GenerateFieldPort generator, Field field) {
String name = generator.name().isEmpty() ? field.getName() : generator.name();
boolean hasDefault = generator.hasDefault();
addFieldPort(name, field, hasDefault, false);
}
private final void addProgramGenerator(GenerateProgramPort generator, Field field) {
String name = generator.name();
String varName = generator.variableName().isEmpty() ? name
: generator.variableName();
Class varType = generator.type();
boolean hasDefault = generator.hasDefault();
addProgramPort(name, varName, field, varType, hasDefault);
}
private final void setInitialInputValues(KeyValueMap values) {
for (Entry<String, Object> entry : values.entrySet()) {
setInputValue(entry.getKey(), entry.getValue());
}
}
private final void setImmediateInputValue(String name, Object value) {
if (mLogVerbose) Log.v(TAG, "Setting immediate value " + value + " for port " + name + "!");
FilterPort port = getInputPort(name);
port.open();
port.setFrame(SimpleFrame.wrapObject(value, null));
}
private final void transferInputFrames(FilterContext context) {
for (InputPort inputPort : mInputPorts.values()) {
inputPort.transfer(context);
}
}
private final Frame wrapInputValue(String inputName, Object value) {
MutableFrameFormat inputFormat = ObjectFormat.fromObject(value, FrameFormat.TARGET_SIMPLE);
if (value == null) {
// If the value is null, the format cannot guess the class, so we adjust it to the
// class of the input port here
FrameFormat portFormat = getInputPort(inputName).getPortFormat();
Class portClass = (portFormat == null) ? null : portFormat.getObjectClass();
inputFormat.setObjectClass(portClass);
}
// Serialize if serializable, and type is not an immutable primitive.
boolean shouldSerialize = !(value instanceof Number)
&& !(value instanceof Boolean)
&& !(value instanceof String)
&& value instanceof Serializable;
// Create frame wrapper
Frame frame = shouldSerialize
? new SerializedFrame(inputFormat, null)
: new SimpleFrame(inputFormat, null);
frame.setObjectValue(value);
return frame;
}
private final void releasePulledFrames(FilterContext context) {
for (Frame frame : mFramesToRelease) {
context.getFrameManager().releaseFrame(frame);
}
mFramesToRelease.clear();
}
private final boolean inputConditionsMet() {
for (FilterPort port : mInputPorts.values()) {
if (!port.isReady()) {
if (mLogVerbose) Log.v(TAG, "Input condition not met: " + port + "!");
return false;
}
}
return true;
}
private final boolean outputConditionsMet() {
for (FilterPort port : mOutputPorts.values()) {
if (!port.isReady()) {
if (mLogVerbose) Log.v(TAG, "Output condition not met: " + port + "!");
return false;
}
}
return true;
}
private final void closePorts() {
if (mLogVerbose) Log.v(TAG, "Closing all ports on " + this + "!");
for (InputPort inputPort : mInputPorts.values()) {
inputPort.close();
}
for (OutputPort outputPort : mOutputPorts.values()) {
outputPort.close();
}
}
private final boolean filterMustClose() {
for (InputPort inputPort : mInputPorts.values()) {
if (inputPort.filterMustClose()) {
if (mLogVerbose) Log.v(TAG, "Filter " + this + " must close due to port " + inputPort);
return true;
}
}
for (OutputPort outputPort : mOutputPorts.values()) {
if (outputPort.filterMustClose()) {
if (mLogVerbose) Log.v(TAG, "Filter " + this + " must close due to port " + outputPort);
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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 android.filterfw.core.Filter;
import android.filterfw.core.Frame;
import android.filterfw.core.FrameManager;
import android.filterfw.core.GLEnvironment;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
/**
* @hide
*/
public class FilterContext {
private FrameManager mFrameManager;
private GLEnvironment mGLEnvironment;
private HashMap<String, Frame> mStoredFrames = new HashMap<String, Frame>();
private Set<FilterGraph> mGraphs = new HashSet<FilterGraph>();
public FrameManager getFrameManager() {
return mFrameManager;
}
public void setFrameManager(FrameManager manager) {
if (manager == null) {
throw new NullPointerException("Attempting to set null FrameManager!");
} else if (manager.getContext() != null) {
throw new IllegalArgumentException("Attempting to set FrameManager which is already "
+ "bound to another FilterContext!");
} else {
mFrameManager = manager;
mFrameManager.setContext(this);
}
}
public GLEnvironment getGLEnvironment() {
return mGLEnvironment;
}
public void initGLEnvironment(GLEnvironment environment) {
if (mGLEnvironment == null) {
mGLEnvironment = environment;
} else {
throw new RuntimeException("Attempting to re-initialize GL Environment for " +
"FilterContext!");
}
}
public interface OnFrameReceivedListener {
public void onFrameReceived(Filter filter, Frame frame, Object userData);
}
public synchronized void storeFrame(String key, Frame frame) {
Frame storedFrame = fetchFrame(key);
if (storedFrame != null) {
storedFrame.release();
}
frame.onFrameStore();
mStoredFrames.put(key, frame.retain());
}
public synchronized Frame fetchFrame(String key) {
Frame frame = mStoredFrames.get(key);
if (frame != null) {
frame.onFrameFetch();
}
return frame;
}
public synchronized void removeFrame(String key) {
Frame frame = mStoredFrames.get(key);
if (frame != null) {
mStoredFrames.remove(key);
frame.release();
}
}
public synchronized void tearDown() {
// Release stored frames
for (Frame frame : mStoredFrames.values()) {
frame.release();
}
mStoredFrames.clear();
// Release graphs
for (FilterGraph graph : mGraphs) {
graph.tearDown(this);
}
mGraphs.clear();
// Release frame manager
if (mFrameManager != null) {
mFrameManager.tearDown();
mFrameManager = null;
}
// Release GL context
if (mGLEnvironment != null) {
mGLEnvironment.tearDown();
mGLEnvironment = null;
}
}
final void addGraph(FilterGraph graph) {
mGraphs.add(graph);
}
}

View File

@@ -0,0 +1,145 @@
/*
* 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 android.filterfw.core.Filter;
import android.util.Log;
import dalvik.system.PathClassLoader;
import java.lang.reflect.Constructor;
import java.lang.ClassLoader;
import java.lang.Thread;
import java.util.HashSet;
/**
* @hide
*/
public class FilterFactory {
private static FilterFactory mSharedFactory;
private HashSet<String> mPackages = new HashSet<String>();
private static ClassLoader mCurrentClassLoader;
private static HashSet<String> mLibraries;
private static Object mClassLoaderGuard;
static {
mCurrentClassLoader = Thread.currentThread().getContextClassLoader();
mLibraries = new HashSet<String>();
mClassLoaderGuard = new Object();
}
private static final String TAG = "FilterFactory";
private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
public static FilterFactory sharedFactory() {
if (mSharedFactory == null) {
mSharedFactory = new FilterFactory();
}
return mSharedFactory;
}
/**
* Adds a new Java library to the list to be scanned for filters.
* libraryPath must be an absolute path of the jar file. This needs to be
* static because only one classloader per process can open a shared native
* library, which a filter may well have.
*/
public static void addFilterLibrary(String libraryPath) {
if (mLogVerbose) Log.v(TAG, "Adding filter library " + libraryPath);
synchronized(mClassLoaderGuard) {
if (mLibraries.contains(libraryPath)) {
if (mLogVerbose) Log.v(TAG, "Library already added");
return;
}
mLibraries.add(libraryPath);
// Chain another path loader to the current chain
mCurrentClassLoader = new PathClassLoader(libraryPath, mCurrentClassLoader);
}
}
public void addPackage(String packageName) {
if (mLogVerbose) Log.v(TAG, "Adding package " + packageName);
/* TODO: This should use a getPackage call in the caller's context, but no such method exists.
Package pkg = Package.getPackage(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown filter package '" + packageName + "'!");
}
*/
mPackages.add(packageName);
}
public Filter createFilterByClassName(String className, String filterName) {
if (mLogVerbose) Log.v(TAG, "Looking up class " + className);
Class filterClass = null;
// Look for the class in the imported packages
for (String packageName : mPackages) {
try {
if (mLogVerbose) Log.v(TAG, "Trying "+packageName + "." + className);
synchronized(mClassLoaderGuard) {
filterClass = mCurrentClassLoader.loadClass(packageName + "." + className);
}
} catch (ClassNotFoundException e) {
continue;
}
// Exit loop if class was found.
if (filterClass != null) {
break;
}
}
if (filterClass == null) {
throw new IllegalArgumentException("Unknown filter class '" + className + "'!");
}
return createFilterByClass(filterClass, filterName);
}
public Filter createFilterByClass(Class filterClass, String filterName) {
// Make sure this is a Filter subclass
try {
filterClass.asSubclass(Filter.class);
} catch (ClassCastException e) {
throw new IllegalArgumentException("Attempting to allocate class '" + filterClass
+ "' which is not a subclass of Filter!");
}
// Look for the correct constructor
Constructor filterConstructor = null;
try {
filterConstructor = filterClass.getConstructor(String.class);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("The filter class '" + filterClass
+ "' does not have a constructor of the form <init>(String name)!");
}
// Construct the filter
Filter filter = null;
try {
filter = (Filter)filterConstructor.newInstance(filterName);
} catch (Throwable t) {
// Condition checked below
}
if (filter == null) {
throw new IllegalArgumentException("Could not construct the filter '"
+ filterName + "'!");
}
return filter;
}
}

View File

@@ -0,0 +1,139 @@
/*
* 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.Map.Entry;
/**
* @hide
*/
public class FilterFunction {
private Filter mFilter;
private FilterContext mFilterContext;
private boolean mFilterIsSetup = false;
private FrameHolderPort[] mResultHolders;
private class FrameHolderPort extends StreamPort {
public FrameHolderPort() {
super(null, "holder");
}
}
public FilterFunction(FilterContext context, Filter filter) {
mFilterContext = context;
mFilter = filter;
}
public Frame execute(KeyValueMap inputMap) {
int filterOutCount = mFilter.getNumberOfOutputs();
// Sanity checks
if (filterOutCount > 1) {
throw new RuntimeException("Calling execute on filter " + mFilter + " with multiple "
+ "outputs! Use executeMulti() instead!");
}
// Setup filter
if (!mFilterIsSetup) {
connectFilterOutputs();
mFilterIsSetup = true;
}
// Make sure GL environment is active
boolean didActivateGLEnv = false;
GLEnvironment glEnv = mFilterContext.getGLEnvironment();
if (glEnv != null && !glEnv.isActive()) {
glEnv.activate();
didActivateGLEnv = true;
}
// Setup the inputs
for (Entry<String, Object> entry : inputMap.entrySet()) {
if (entry.getValue() instanceof Frame) {
mFilter.pushInputFrame(entry.getKey(), (Frame)entry.getValue());
} else {
mFilter.pushInputValue(entry.getKey(), entry.getValue());
}
}
// Process the filter
if (mFilter.getStatus() != Filter.STATUS_PROCESSING) {
mFilter.openOutputs();
}
mFilter.performProcess(mFilterContext);
// Create result handle
Frame result = null;
if (filterOutCount == 1 && mResultHolders[0].hasFrame()) {
result = mResultHolders[0].pullFrame();
}
// Deactivate GL environment if activated
if (didActivateGLEnv) {
glEnv.deactivate();
}
return result;
}
public Frame executeWithArgList(Object... inputs) {
return execute(KeyValueMap.fromKeyValues(inputs));
}
public void close() {
mFilter.performClose(mFilterContext);
}
public FilterContext getContext() {
return mFilterContext;
}
public Filter getFilter() {
return mFilter;
}
public void setInputFrame(String input, Frame frame) {
mFilter.setInputFrame(input, frame);
}
public void setInputValue(String input, Object value) {
mFilter.setInputValue(input, value);
}
public void tearDown() {
mFilter.performTearDown(mFilterContext);
mFilter = null;
}
@Override
public String toString() {
return mFilter.getName();
}
private void connectFilterOutputs() {
int i = 0;
mResultHolders = new FrameHolderPort[mFilter.getNumberOfOutputs()];
for (OutputPort outputPort : mFilter.getOutputPorts()) {
mResultHolders[i] = new FrameHolderPort();
outputPort.connectTo(mResultHolders[i]);
++i;
}
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,134 @@
/*
* 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 android.filterfw.core.Filter;
import android.filterfw.core.FrameFormat;
import android.util.Log;
/**
* @hide
*/
public abstract class FilterPort {
protected Filter mFilter;
protected String mName;
protected FrameFormat mPortFormat;
protected boolean mIsBlocking = true;
protected boolean mIsOpen = false;
protected boolean mChecksType = false;
private boolean mLogVerbose;
private static final String TAG = "FilterPort";
public FilterPort(Filter filter, String name) {
mName = name;
mFilter = filter;
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
}
public boolean isAttached() {
return mFilter != null;
}
public FrameFormat getPortFormat() {
return mPortFormat;
}
public void setPortFormat(FrameFormat format) {
mPortFormat = format;
}
public Filter getFilter() {
return mFilter;
}
public String getName() {
return mName;
}
public void setBlocking(boolean blocking) {
mIsBlocking = blocking;
}
public void setChecksType(boolean checksType) {
mChecksType = checksType;
}
public void open() {
if (!mIsOpen) {
if (mLogVerbose) Log.v(TAG, "Opening " + this);
}
mIsOpen = true;
}
public void close() {
if (mIsOpen) {
if (mLogVerbose) Log.v(TAG, "Closing " + this);
}
mIsOpen = false;
}
public boolean isOpen() {
return mIsOpen;
}
public boolean isBlocking() {
return mIsBlocking;
}
public abstract boolean filterMustClose();
public abstract boolean isReady();
public abstract void pushFrame(Frame frame);
public abstract void setFrame(Frame frame);
public abstract Frame pullFrame();
public abstract boolean hasFrame();
public abstract void clear();
public String toString() {
return "port '" + mName + "' of " + mFilter;
}
protected void assertPortIsOpen() {
if (!isOpen()) {
throw new RuntimeException("Illegal operation on closed " + this + "!");
}
}
protected void checkFrameType(Frame frame, boolean forceCheck) {
if ((mChecksType || forceCheck)
&& mPortFormat != null
&& !frame.getFormat().isCompatibleWith(mPortFormat)) {
throw new RuntimeException("Frame passed to " + this + " is of incorrect type! "
+ "Expected " + mPortFormat + " but got " + frame.getFormat());
}
}
protected void checkFrameManager(Frame frame, FilterContext context) {
if (frame.getFrameManager() != null
&& frame.getFrameManager() != context.getFrameManager()) {
throw new RuntimeException("Frame " + frame + " is managed by foreign FrameManager! ");
}
}
}

View File

@@ -0,0 +1,157 @@
/*
* 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 android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* @hide
*/
public class FilterSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private static int STATE_ALLOCATED = 0;
private static int STATE_CREATED = 1;
private static int STATE_INITIALIZED = 2;
private int mState = STATE_ALLOCATED;
private SurfaceHolder.Callback mListener;
private GLEnvironment mGLEnv;
private int mFormat;
private int mWidth;
private int mHeight;
private int mSurfaceId = -1;
public FilterSurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
}
public FilterSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
}
public synchronized void bindToListener(SurfaceHolder.Callback listener, GLEnvironment glEnv) {
// Make sure we are not bound already
if (listener == null) {
throw new NullPointerException("Attempting to bind null filter to SurfaceView!");
} else if (mListener != null && mListener != listener) {
throw new RuntimeException(
"Attempting to bind filter " + listener + " to SurfaceView with another open "
+ "filter " + mListener + " attached already!");
}
// Set listener
mListener = listener;
// Set GLEnv
if (mGLEnv != null && mGLEnv != glEnv) {
mGLEnv.unregisterSurfaceId(mSurfaceId);
}
mGLEnv = glEnv;
// Check if surface has been created already
if (mState >= STATE_CREATED) {
// Register with env (double registration will be ignored by GLEnv, so we can simply
// try to do it here).
registerSurface();
// Forward surface created to listener
mListener.surfaceCreated(getHolder());
// Forward surface changed to listener
if (mState == STATE_INITIALIZED) {
mListener.surfaceChanged(getHolder(), mFormat, mWidth, mHeight);
}
}
}
public synchronized void unbind() {
mListener = null;
}
public synchronized int getSurfaceId() {
return mSurfaceId;
}
public synchronized GLEnvironment getGLEnv() {
return mGLEnv;
}
@Override
public synchronized void surfaceCreated(SurfaceHolder holder) {
mState = STATE_CREATED;
// Register with GLEnvironment if we have it already
if (mGLEnv != null) {
registerSurface();
}
// Forward callback to listener
if (mListener != null) {
mListener.surfaceCreated(holder);
}
}
@Override
public synchronized void surfaceChanged(SurfaceHolder holder,
int format,
int width,
int height) {
// Remember these values
mFormat = format;
mWidth = width;
mHeight = height;
mState = STATE_INITIALIZED;
// Forward to renderer
if (mListener != null) {
mListener.surfaceChanged(holder, format, width, height);
}
}
@Override
public synchronized void surfaceDestroyed(SurfaceHolder holder) {
mState = STATE_ALLOCATED;
// Forward to renderer
if (mListener != null) {
mListener.surfaceDestroyed(holder);
}
// Get rid of internal objects associated with this surface
unregisterSurface();
}
private void registerSurface() {
mSurfaceId = mGLEnv.registerSurface(getHolder().getSurface());
if (mSurfaceId < 0) {
throw new RuntimeException("Could not register Surface: " + getHolder().getSurface() +
" in FilterSurfaceView!");
}
}
private void unregisterSurface() {
if (mGLEnv != null && mSurfaceId > 0) {
mGLEnv.unregisterSurfaceId(mSurfaceId);
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.lang.reflect.Field;
/**
* @hide
*/
public class FinalPort extends FieldPort {
public FinalPort(Filter filter, String name, Field field, boolean hasDefault) {
super(filter, name, field, hasDefault);
}
@Override
protected synchronized void setFieldFrame(Frame frame, boolean isAssignment) {
assertPortIsOpen();
checkFrameType(frame, isAssignment);
if (mFilter.getStatus() != Filter.STATUS_PREINIT) {
throw new RuntimeException("Attempting to modify " + this + "!");
} else {
super.setFieldFrame(frame, isAssignment);
super.transfer(null);
}
}
@Override
public String toString() {
return "final " + super.toString();
}
}

View File

@@ -0,0 +1,236 @@
/*
* 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 android.filterfw.core.FrameFormat;
import android.filterfw.core.FrameManager;
import android.graphics.Bitmap;
import android.util.Log;
import java.nio.ByteBuffer;
/**
* @hide
*/
public abstract class Frame {
public final static int NO_BINDING = 0;
public final static long TIMESTAMP_NOT_SET = -2;
public final static long TIMESTAMP_UNKNOWN = -1;
private FrameFormat mFormat;
private FrameManager mFrameManager;
private boolean mReadOnly = false;
private boolean mReusable = false;
private int mRefCount = 1;
private int mBindingType = NO_BINDING;
private long mBindingId = 0;
private long mTimestamp = TIMESTAMP_NOT_SET;
Frame(FrameFormat format, FrameManager frameManager) {
mFormat = format.mutableCopy();
mFrameManager = frameManager;
}
Frame(FrameFormat format, FrameManager frameManager, int bindingType, long bindingId) {
mFormat = format.mutableCopy();
mFrameManager = frameManager;
mBindingType = bindingType;
mBindingId = bindingId;
}
public FrameFormat getFormat() {
return mFormat;
}
public int getCapacity() {
return getFormat().getSize();
}
public boolean isReadOnly() {
return mReadOnly;
}
public int getBindingType() {
return mBindingType;
}
public long getBindingId() {
return mBindingId;
}
public void setObjectValue(Object object) {
assertFrameMutable();
// Attempt to set the value using a specific setter (which may be more optimized), and
// fall back to the setGenericObjectValue(...) in case of no match.
if (object instanceof int[]) {
setInts((int[])object);
} else if (object instanceof float[]) {
setFloats((float[])object);
} else if (object instanceof ByteBuffer) {
setData((ByteBuffer)object);
} else if (object instanceof Bitmap) {
setBitmap((Bitmap)object);
} else {
setGenericObjectValue(object);
}
}
public abstract Object getObjectValue();
public abstract void setInts(int[] ints);
public abstract int[] getInts();
public abstract void setFloats(float[] floats);
public abstract float[] getFloats();
public abstract void setData(ByteBuffer buffer, int offset, int length);
public void setData(ByteBuffer buffer) {
setData(buffer, 0, buffer.limit());
}
public void setData(byte[] bytes, int offset, int length) {
setData(ByteBuffer.wrap(bytes, offset, length));
}
public abstract ByteBuffer getData();
public abstract void setBitmap(Bitmap bitmap);
public abstract Bitmap getBitmap();
public void setTimestamp(long timestamp) {
mTimestamp = timestamp;
}
public long getTimestamp() {
return mTimestamp;
}
public void setDataFromFrame(Frame frame) {
setData(frame.getData());
}
protected boolean requestResize(int[] newDimensions) {
return false;
}
public int getRefCount() {
return mRefCount;
}
public Frame release() {
if (mFrameManager != null) {
return mFrameManager.releaseFrame(this);
} else {
return this;
}
}
public Frame retain() {
if (mFrameManager != null) {
return mFrameManager.retainFrame(this);
} else {
return this;
}
}
public FrameManager getFrameManager() {
return mFrameManager;
}
protected void assertFrameMutable() {
if (isReadOnly()) {
throw new RuntimeException("Attempting to modify read-only frame!");
}
}
protected void setReusable(boolean reusable) {
mReusable = reusable;
}
protected void setFormat(FrameFormat format) {
mFormat = format.mutableCopy();
}
protected void setGenericObjectValue(Object value) {
throw new RuntimeException(
"Cannot set object value of unsupported type: " + value.getClass());
}
protected static Bitmap convertBitmapToRGBA(Bitmap bitmap) {
if (bitmap.getConfig() == Bitmap.Config.ARGB_8888) {
return bitmap;
} else {
Bitmap result = bitmap.copy(Bitmap.Config.ARGB_8888, false);
if (result == null) {
throw new RuntimeException("Error converting bitmap to RGBA!");
} else if (result.getRowBytes() != result.getWidth() * 4) {
throw new RuntimeException("Unsupported row byte count in bitmap!");
}
return result;
}
}
protected void reset(FrameFormat newFormat) {
mFormat = newFormat.mutableCopy();
mReadOnly = false;
mRefCount = 1;
}
/**
* Called just before a frame is stored, such as when storing to a cache or context.
*/
protected void onFrameStore() {
}
/**
* Called when a frame is fetched from an internal store such as a cache.
*/
protected void onFrameFetch() {
}
// Core internal methods ///////////////////////////////////////////////////////////////////////
protected abstract boolean hasNativeAllocation();
protected abstract void releaseNativeAllocation();
final int incRefCount() {
++mRefCount;
return mRefCount;
}
final int decRefCount() {
--mRefCount;
return mRefCount;
}
final boolean isReusable() {
return mReusable;
}
final void markReadOnly() {
mReadOnly = true;
}
}

View File

@@ -0,0 +1,439 @@
/*
* 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 android.filterfw.core.KeyValueMap;
import android.filterfw.core.MutableFrameFormat;
import java.util.Arrays;
import java.util.Map.Entry;
/**
* @hide
*/
public class FrameFormat {
public static final int TYPE_UNSPECIFIED = 0;
public static final int TYPE_BIT = 1;
public static final int TYPE_BYTE = 2;
public static final int TYPE_INT16 = 3;
public static final int TYPE_INT32 = 4;
public static final int TYPE_FLOAT = 5;
public static final int TYPE_DOUBLE = 6;
public static final int TYPE_POINTER = 7;
public static final int TYPE_OBJECT = 8;
public static final int TARGET_UNSPECIFIED = 0;
public static final int TARGET_SIMPLE = 1;
public static final int TARGET_NATIVE = 2;
public static final int TARGET_GPU = 3;
public static final int TARGET_VERTEXBUFFER = 4;
public static final int TARGET_RS = 5;
public static final int SIZE_UNSPECIFIED = 0;
// TODO: When convenience formats are used, consider changing this to 0 and have the convenience
// intializers use a proper BPS.
public static final int BYTES_PER_SAMPLE_UNSPECIFIED = 1;
protected static final int SIZE_UNKNOWN = -1;
protected int mBaseType = TYPE_UNSPECIFIED;
protected int mBytesPerSample = 1;
protected int mSize = SIZE_UNKNOWN;
protected int mTarget = TARGET_UNSPECIFIED;
protected int[] mDimensions;
protected KeyValueMap mMetaData;
protected Class mObjectClass;
protected FrameFormat() {
}
public FrameFormat(int baseType, int target) {
mBaseType = baseType;
mTarget = target;
initDefaults();
}
public static FrameFormat unspecified() {
return new FrameFormat(TYPE_UNSPECIFIED, TARGET_UNSPECIFIED);
}
public int getBaseType() {
return mBaseType;
}
public boolean isBinaryDataType() {
return mBaseType >= TYPE_BIT && mBaseType <= TYPE_DOUBLE;
}
public int getBytesPerSample() {
return mBytesPerSample;
}
public int getValuesPerSample() {
return mBytesPerSample / bytesPerSampleOf(mBaseType);
}
public int getTarget() {
return mTarget;
}
public int[] getDimensions() {
return mDimensions;
}
public int getDimension(int i) {
return mDimensions[i];
}
public int getDimensionCount() {
return mDimensions == null ? 0 : mDimensions.length;
}
public boolean hasMetaKey(String key) {
return mMetaData != null ? mMetaData.containsKey(key) : false;
}
public boolean hasMetaKey(String key, Class expectedClass) {
if (mMetaData != null && mMetaData.containsKey(key)) {
if (!expectedClass.isAssignableFrom(mMetaData.get(key).getClass())) {
throw new RuntimeException(
"FrameFormat meta-key '" + key + "' is of type " +
mMetaData.get(key).getClass() + " but expected to be of type " +
expectedClass + "!");
}
return true;
}
return false;
}
public Object getMetaValue(String key) {
return mMetaData != null ? mMetaData.get(key) : null;
}
public int getNumberOfDimensions() {
return mDimensions != null ? mDimensions.length : 0;
}
public int getLength() {
return (mDimensions != null && mDimensions.length >= 1) ? mDimensions[0] : -1;
}
public int getWidth() {
return getLength();
}
public int getHeight() {
return (mDimensions != null && mDimensions.length >= 2) ? mDimensions[1] : -1;
}
public int getDepth() {
return (mDimensions != null && mDimensions.length >= 3) ? mDimensions[2] : -1;
}
public int getSize() {
if (mSize == SIZE_UNKNOWN) mSize = calcSize(mDimensions);
return mSize;
}
public Class getObjectClass() {
return mObjectClass;
}
public MutableFrameFormat mutableCopy() {
MutableFrameFormat result = new MutableFrameFormat();
result.setBaseType(getBaseType());
result.setTarget(getTarget());
result.setBytesPerSample(getBytesPerSample());
result.setDimensions(getDimensions());
result.setObjectClass(getObjectClass());
result.mMetaData = mMetaData == null ? null : (KeyValueMap)mMetaData.clone();
return result;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof FrameFormat)) {
return false;
}
FrameFormat format = (FrameFormat)object;
return format.mBaseType == mBaseType &&
format.mTarget == mTarget &&
format.mBytesPerSample == mBytesPerSample &&
Arrays.equals(format.mDimensions, mDimensions) &&
format.mMetaData.equals(mMetaData);
}
@Override
public int hashCode() {
return 4211 ^ mBaseType ^ mBytesPerSample ^ getSize();
}
public boolean isCompatibleWith(FrameFormat specification) {
// Check base type
if (specification.getBaseType() != TYPE_UNSPECIFIED
&& getBaseType() != specification.getBaseType()) {
return false;
}
// Check target
if (specification.getTarget() != TARGET_UNSPECIFIED
&& getTarget() != specification.getTarget()) {
return false;
}
// Check bytes per sample
if (specification.getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED
&& getBytesPerSample() != specification.getBytesPerSample()) {
return false;
}
// Check number of dimensions
if (specification.getDimensionCount() > 0
&& getDimensionCount() != specification.getDimensionCount()) {
return false;
}
// Check dimensions
for (int i = 0; i < specification.getDimensionCount(); ++i) {
int specDim = specification.getDimension(i);
if (specDim != SIZE_UNSPECIFIED && getDimension(i) != specDim) {
return false;
}
}
// Check class
if (specification.getObjectClass() != null) {
if (getObjectClass() == null
|| !specification.getObjectClass().isAssignableFrom(getObjectClass())) {
return false;
}
}
// Check meta-data
if (specification.mMetaData != null) {
for (String specKey : specification.mMetaData.keySet()) {
if (mMetaData == null
|| !mMetaData.containsKey(specKey)
|| !mMetaData.get(specKey).equals(specification.mMetaData.get(specKey))) {
return false;
}
}
}
// Passed all the tests
return true;
}
public boolean mayBeCompatibleWith(FrameFormat specification) {
// Check base type
if (specification.getBaseType() != TYPE_UNSPECIFIED
&& getBaseType() != TYPE_UNSPECIFIED
&& getBaseType() != specification.getBaseType()) {
return false;
}
// Check target
if (specification.getTarget() != TARGET_UNSPECIFIED
&& getTarget() != TARGET_UNSPECIFIED
&& getTarget() != specification.getTarget()) {
return false;
}
// Check bytes per sample
if (specification.getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED
&& getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED
&& getBytesPerSample() != specification.getBytesPerSample()) {
return false;
}
// Check number of dimensions
if (specification.getDimensionCount() > 0
&& getDimensionCount() > 0
&& getDimensionCount() != specification.getDimensionCount()) {
return false;
}
// Check dimensions
for (int i = 0; i < specification.getDimensionCount(); ++i) {
int specDim = specification.getDimension(i);
if (specDim != SIZE_UNSPECIFIED
&& getDimension(i) != SIZE_UNSPECIFIED
&& getDimension(i) != specDim) {
return false;
}
}
// Check class
if (specification.getObjectClass() != null && getObjectClass() != null) {
if (!specification.getObjectClass().isAssignableFrom(getObjectClass())) {
return false;
}
}
// Check meta-data
if (specification.mMetaData != null && mMetaData != null) {
for (String specKey : specification.mMetaData.keySet()) {
if (mMetaData.containsKey(specKey)
&& !mMetaData.get(specKey).equals(specification.mMetaData.get(specKey))) {
return false;
}
}
}
// Passed all the tests
return true;
}
public static int bytesPerSampleOf(int baseType) {
// Defaults based on base-type
switch (baseType) {
case TYPE_BIT:
case TYPE_BYTE:
return 1;
case TYPE_INT16:
return 2;
case TYPE_INT32:
case TYPE_FLOAT:
case TYPE_POINTER:
return 4;
case TYPE_DOUBLE:
return 8;
default:
return 1;
}
}
public static String dimensionsToString(int[] dimensions) {
StringBuffer buffer = new StringBuffer();
if (dimensions != null) {
int n = dimensions.length;
for (int i = 0; i < n; ++i) {
if (dimensions[i] == SIZE_UNSPECIFIED) {
buffer.append("[]");
} else {
buffer.append("[" + String.valueOf(dimensions[i]) + "]");
}
}
}
return buffer.toString();
}
public static String baseTypeToString(int baseType) {
switch (baseType) {
case TYPE_UNSPECIFIED: return "unspecified";
case TYPE_BIT: return "bit";
case TYPE_BYTE: return "byte";
case TYPE_INT16: return "int";
case TYPE_INT32: return "int";
case TYPE_FLOAT: return "float";
case TYPE_DOUBLE: return "double";
case TYPE_POINTER: return "pointer";
case TYPE_OBJECT: return "object";
default: return "unknown";
}
}
public static String targetToString(int target) {
switch (target) {
case TARGET_UNSPECIFIED: return "unspecified";
case TARGET_SIMPLE: return "simple";
case TARGET_NATIVE: return "native";
case TARGET_GPU: return "gpu";
case TARGET_VERTEXBUFFER: return "vbo";
case TARGET_RS: return "renderscript";
default: return "unknown";
}
}
public static String metaDataToString(KeyValueMap metaData) {
if (metaData == null) {
return "";
} else {
StringBuffer buffer = new StringBuffer();
buffer.append("{ ");
for (Entry<String, Object> entry : metaData.entrySet()) {
buffer.append(entry.getKey() + ": " + entry.getValue() + " ");
}
buffer.append("}");
return buffer.toString();
}
}
public static int readTargetString(String targetString) {
if (targetString.equalsIgnoreCase("CPU") || targetString.equalsIgnoreCase("NATIVE")) {
return FrameFormat.TARGET_NATIVE;
} else if (targetString.equalsIgnoreCase("GPU")) {
return FrameFormat.TARGET_GPU;
} else if (targetString.equalsIgnoreCase("SIMPLE")) {
return FrameFormat.TARGET_SIMPLE;
} else if (targetString.equalsIgnoreCase("VERTEXBUFFER")) {
return FrameFormat.TARGET_VERTEXBUFFER;
} else if (targetString.equalsIgnoreCase("UNSPECIFIED")) {
return FrameFormat.TARGET_UNSPECIFIED;
} else {
throw new RuntimeException("Unknown target type '" + targetString + "'!");
}
}
// TODO: FromString
public String toString() {
int valuesPerSample = getValuesPerSample();
String sampleCountString = valuesPerSample == 1 ? "" : String.valueOf(valuesPerSample);
String targetString = mTarget == TARGET_UNSPECIFIED ? "" : (targetToString(mTarget) + " ");
String classString = mObjectClass == null
? ""
: (" class(" + mObjectClass.getSimpleName() + ") ");
return targetString
+ baseTypeToString(mBaseType)
+ sampleCountString
+ dimensionsToString(mDimensions)
+ classString
+ metaDataToString(mMetaData);
}
private void initDefaults() {
mBytesPerSample = bytesPerSampleOf(mBaseType);
}
// Core internal methods ///////////////////////////////////////////////////////////////////////
int calcSize(int[] dimensions) {
if (dimensions != null && dimensions.length > 0) {
int size = getBytesPerSample();
for (int dim : dimensions) {
size *= dim;
}
return size;
}
return 0;
}
boolean isReplaceableBy(FrameFormat format) {
return mTarget == format.mTarget
&& getSize() == format.getSize()
&& Arrays.equals(format.mDimensions, mDimensions);
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.MutableFrameFormat;
/**
* @hide
*/
public abstract class FrameManager {
private FilterContext mContext;
public abstract Frame newFrame(FrameFormat format);
public abstract Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId);
public Frame duplicateFrame(Frame frame) {
Frame result = newFrame(frame.getFormat());
result.setDataFromFrame(frame);
return result;
}
public Frame duplicateFrameToTarget(Frame frame, int newTarget) {
MutableFrameFormat newFormat = frame.getFormat().mutableCopy();
newFormat.setTarget(newTarget);
Frame result = newFrame(newFormat);
result.setDataFromFrame(frame);
return result;
}
public abstract Frame retainFrame(Frame frame);
public abstract Frame releaseFrame(Frame frame);
public FilterContext getContext() {
return mContext;
}
public GLEnvironment getGLEnvironment() {
return mContext != null ? mContext.getGLEnvironment() : null;
}
public void tearDown() {
}
void setContext(FilterContext context) {
mContext = context;
}
}

View File

@@ -0,0 +1,180 @@
/*
* 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 android.filterfw.core.NativeAllocatorTag;
import android.graphics.SurfaceTexture;
import android.os.Looper;
import android.util.Log;
import android.view.Surface;
import android.media.MediaRecorder;
/**
* @hide
*/
public class GLEnvironment {
private int glEnvId;
public GLEnvironment() {
nativeAllocate();
}
private GLEnvironment(NativeAllocatorTag tag) {
}
public synchronized void tearDown() {
if (glEnvId != -1) {
nativeDeallocate();
glEnvId = -1;
}
}
@Override
protected void finalize() throws Throwable {
tearDown();
}
public void initWithNewContext() {
if (!nativeInitWithNewContext()) {
throw new RuntimeException("Could not initialize GLEnvironment with new context!");
}
}
public void initWithCurrentContext() {
if (!nativeInitWithCurrentContext()) {
throw new RuntimeException("Could not initialize GLEnvironment with current context!");
}
}
public boolean isActive() {
return nativeIsActive();
}
public boolean isContextActive() {
return nativeIsContextActive();
}
public static boolean isAnyContextActive() {
return nativeIsAnyContextActive();
}
public void activate() {
if (Looper.myLooper() != null && Looper.myLooper().equals(Looper.getMainLooper())) {
Log.e("FilterFramework", "Activating GL context in UI thread!");
}
if (!nativeActivate()) {
throw new RuntimeException("Could not activate GLEnvironment!");
}
}
public void deactivate() {
if (!nativeDeactivate()) {
throw new RuntimeException("Could not deactivate GLEnvironment!");
}
}
public void swapBuffers() {
if (!nativeSwapBuffers()) {
throw new RuntimeException("Error swapping EGL buffers!");
}
}
public int registerSurface(Surface surface) {
int result = nativeAddSurface(surface);
if (result < 0) {
throw new RuntimeException("Error registering surface " + surface + "!");
}
return result;
}
public int registerSurfaceTexture(SurfaceTexture surfaceTexture, int width, int height) {
Surface surface = new Surface(surfaceTexture);
int result = nativeAddSurfaceWidthHeight(surface, width, height);
surface.release();
if (result < 0) {
throw new RuntimeException("Error registering surfaceTexture " + surfaceTexture + "!");
}
return result;
}
public int registerSurfaceFromMediaRecorder(MediaRecorder mediaRecorder) {
int result = nativeAddSurfaceFromMediaRecorder(mediaRecorder);
if (result < 0) {
throw new RuntimeException("Error registering surface from "
+ "MediaRecorder" + mediaRecorder + "!");
}
return result;
}
public void activateSurfaceWithId(int surfaceId) {
if (!nativeActivateSurfaceId(surfaceId)) {
throw new RuntimeException("Could not activate surface " + surfaceId + "!");
}
}
public void unregisterSurfaceId(int surfaceId) {
if (!nativeRemoveSurfaceId(surfaceId)) {
throw new RuntimeException("Could not unregister surface " + surfaceId + "!");
}
}
public void setSurfaceTimestamp(long timestamp) {
if (!nativeSetSurfaceTimestamp(timestamp)) {
throw new RuntimeException("Could not set timestamp for current surface!");
}
}
static {
System.loadLibrary("filterfw");
}
private native boolean nativeInitWithNewContext();
private native boolean nativeInitWithCurrentContext();
private native boolean nativeIsActive();
private native boolean nativeIsContextActive();
private static native boolean nativeIsAnyContextActive();
private native boolean nativeActivate();
private native boolean nativeDeactivate();
private native boolean nativeSwapBuffers();
private native boolean nativeAllocate();
private native boolean nativeDeallocate();
private native int nativeAddSurface(Surface surface);
private native int nativeAddSurfaceWidthHeight(Surface surface, int width, int height);
private native int nativeAddSurfaceFromMediaRecorder(MediaRecorder mediaRecorder);
private native boolean nativeDisconnectSurfaceMediaSource(MediaRecorder mediaRecorder);
private native boolean nativeActivateSurfaceId(int surfaceId);
private native boolean nativeRemoveSurfaceId(int surfaceId);
private native boolean nativeSetSurfaceTimestamp(long timestamp);
}

View File

@@ -0,0 +1,417 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.FrameManager;
import android.filterfw.core.NativeFrame;
import android.filterfw.core.StopWatchMap;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.graphics.Rect;
import java.nio.ByteBuffer;
class GLFrameTimer {
private static StopWatchMap mTimer = null;
public static StopWatchMap get() {
if (mTimer == null) {
mTimer = new StopWatchMap();
}
return mTimer;
}
}
/**
* @hide
*/
public class GLFrame extends Frame {
// GL-related binding types
public final static int EXISTING_TEXTURE_BINDING = 100;
public final static int EXISTING_FBO_BINDING = 101;
public final static int NEW_TEXTURE_BINDING = 102; // TODO: REMOVE THIS
public final static int NEW_FBO_BINDING = 103; // TODO: REMOVE THIS
public final static int EXTERNAL_TEXTURE = 104;
private int glFrameId = -1;
/**
* Flag whether we own the texture or not. If we do not, we must be careful when caching or
* storing the frame, as the user may delete, and regenerate it.
*/
private boolean mOwnsTexture = true;
/**
* Keep a reference to the GL environment, so that it does not get deallocated while there
* are still frames living in it.
*/
private GLEnvironment mGLEnvironment;
GLFrame(FrameFormat format, FrameManager frameManager) {
super(format, frameManager);
}
GLFrame(FrameFormat format, FrameManager frameManager, int bindingType, long bindingId) {
super(format, frameManager, bindingType, bindingId);
}
void init(GLEnvironment glEnv) {
FrameFormat format = getFormat();
mGLEnvironment = glEnv;
// Check that we have a valid format
if (format.getBytesPerSample() != 4) {
throw new IllegalArgumentException("GL frames must have 4 bytes per sample!");
} else if (format.getDimensionCount() != 2) {
throw new IllegalArgumentException("GL frames must be 2-dimensional!");
} else if (getFormat().getSize() < 0) {
throw new IllegalArgumentException("Initializing GL frame with zero size!");
}
// Create correct frame
int bindingType = getBindingType();
boolean reusable = true;
if (bindingType == Frame.NO_BINDING) {
initNew(false);
} else if (bindingType == EXTERNAL_TEXTURE) {
initNew(true);
reusable = false;
} else if (bindingType == EXISTING_TEXTURE_BINDING) {
initWithTexture((int)getBindingId());
} else if (bindingType == EXISTING_FBO_BINDING) {
initWithFbo((int)getBindingId());
} else if (bindingType == NEW_TEXTURE_BINDING) {
initWithTexture((int)getBindingId());
} else if (bindingType == NEW_FBO_BINDING) {
initWithFbo((int)getBindingId());
} else {
throw new RuntimeException("Attempting to create GL frame with unknown binding type "
+ bindingType + "!");
}
setReusable(reusable);
}
private void initNew(boolean isExternal) {
if (isExternal) {
if (!nativeAllocateExternal(mGLEnvironment)) {
throw new RuntimeException("Could not allocate external GL frame!");
}
} else {
if (!nativeAllocate(mGLEnvironment, getFormat().getWidth(), getFormat().getHeight())) {
throw new RuntimeException("Could not allocate GL frame!");
}
}
}
private void initWithTexture(int texId) {
int width = getFormat().getWidth();
int height = getFormat().getHeight();
if (!nativeAllocateWithTexture(mGLEnvironment, texId, width, height)) {
throw new RuntimeException("Could not allocate texture backed GL frame!");
}
mOwnsTexture = false;
markReadOnly();
}
private void initWithFbo(int fboId) {
int width = getFormat().getWidth();
int height = getFormat().getHeight();
if (!nativeAllocateWithFbo(mGLEnvironment, fboId, width, height)) {
throw new RuntimeException("Could not allocate FBO backed GL frame!");
}
}
void flushGPU(String message) {
StopWatchMap timer = GLFrameTimer.get();
if (timer.LOG_MFF_RUNNING_TIMES) {
timer.start("glFinish " + message);
GLES20.glFinish();
timer.stop("glFinish " + message);
}
}
@Override
protected synchronized boolean hasNativeAllocation() {
return glFrameId != -1;
}
@Override
protected synchronized void releaseNativeAllocation() {
nativeDeallocate();
glFrameId = -1;
}
public GLEnvironment getGLEnvironment() {
return mGLEnvironment;
}
@Override
public Object getObjectValue() {
assertGLEnvValid();
return ByteBuffer.wrap(getNativeData());
}
@Override
public void setInts(int[] ints) {
assertFrameMutable();
assertGLEnvValid();
if (!setNativeInts(ints)) {
throw new RuntimeException("Could not set int values for GL frame!");
}
}
@Override
public int[] getInts() {
assertGLEnvValid();
flushGPU("getInts");
return getNativeInts();
}
@Override
public void setFloats(float[] floats) {
assertFrameMutable();
assertGLEnvValid();
if (!setNativeFloats(floats)) {
throw new RuntimeException("Could not set int values for GL frame!");
}
}
@Override
public float[] getFloats() {
assertGLEnvValid();
flushGPU("getFloats");
return getNativeFloats();
}
@Override
public void setData(ByteBuffer buffer, int offset, int length) {
assertFrameMutable();
assertGLEnvValid();
byte[] bytes = buffer.array();
if (getFormat().getSize() != bytes.length) {
throw new RuntimeException("Data size in setData does not match GL frame size!");
} else if (!setNativeData(bytes, offset, length)) {
throw new RuntimeException("Could not set GL frame data!");
}
}
@Override
public ByteBuffer getData() {
assertGLEnvValid();
flushGPU("getData");
return ByteBuffer.wrap(getNativeData());
}
@Override
public void setBitmap(Bitmap bitmap) {
assertFrameMutable();
assertGLEnvValid();
if (getFormat().getWidth() != bitmap.getWidth() ||
getFormat().getHeight() != bitmap.getHeight()) {
throw new RuntimeException("Bitmap dimensions do not match GL frame dimensions!");
} else {
Bitmap rgbaBitmap = convertBitmapToRGBA(bitmap);
if (!setNativeBitmap(rgbaBitmap, rgbaBitmap.getByteCount())) {
throw new RuntimeException("Could not set GL frame bitmap data!");
}
}
}
@Override
public Bitmap getBitmap() {
assertGLEnvValid();
flushGPU("getBitmap");
Bitmap result = Bitmap.createBitmap(getFormat().getWidth(),
getFormat().getHeight(),
Bitmap.Config.ARGB_8888);
if (!getNativeBitmap(result)) {
throw new RuntimeException("Could not get bitmap data from GL frame!");
}
return result;
}
@Override
public void setDataFromFrame(Frame frame) {
assertGLEnvValid();
// Make sure frame fits
if (getFormat().getSize() < frame.getFormat().getSize()) {
throw new RuntimeException(
"Attempting to assign frame of size " + frame.getFormat().getSize() + " to " +
"smaller GL frame of size " + getFormat().getSize() + "!");
}
// Invoke optimized implementations if possible
if (frame instanceof NativeFrame) {
nativeCopyFromNative((NativeFrame)frame);
} else if (frame instanceof GLFrame) {
nativeCopyFromGL((GLFrame)frame);
} else if (frame instanceof SimpleFrame) {
setObjectValue(frame.getObjectValue());
} else {
super.setDataFromFrame(frame);
}
}
public void setViewport(int x, int y, int width, int height) {
assertFrameMutable();
setNativeViewport(x, y, width, height);
}
public void setViewport(Rect rect) {
assertFrameMutable();
setNativeViewport(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
}
public void generateMipMap() {
assertFrameMutable();
assertGLEnvValid();
if (!generateNativeMipMap()) {
throw new RuntimeException("Could not generate mip-map for GL frame!");
}
}
public void setTextureParameter(int param, int value) {
assertFrameMutable();
assertGLEnvValid();
if (!setNativeTextureParam(param, value)) {
throw new RuntimeException("Could not set texture value " + param + " = " + value + " " +
"for GLFrame!");
}
}
public int getTextureId() {
return getNativeTextureId();
}
public int getFboId() {
return getNativeFboId();
}
public void focus() {
if (!nativeFocus()) {
throw new RuntimeException("Could not focus on GLFrame for drawing!");
}
}
@Override
public String toString() {
return "GLFrame id: " + glFrameId + " (" + getFormat() + ") with texture ID "
+ getTextureId() + ", FBO ID " + getFboId();
}
@Override
protected void reset(FrameFormat newFormat) {
if (!nativeResetParams()) {
throw new RuntimeException("Could not reset GLFrame texture parameters!");
}
super.reset(newFormat);
}
@Override
protected void onFrameStore() {
if (!mOwnsTexture) {
// Detach texture from FBO in case user manipulates it.
nativeDetachTexFromFbo();
}
}
@Override
protected void onFrameFetch() {
if (!mOwnsTexture) {
// Reattach texture to FBO when using frame again. This may reallocate the texture
// in case it has become invalid.
nativeReattachTexToFbo();
}
}
private void assertGLEnvValid() {
if (!mGLEnvironment.isContextActive()) {
if (GLEnvironment.isAnyContextActive()) {
throw new RuntimeException("Attempting to access " + this + " with foreign GL " +
"context active!");
} else {
throw new RuntimeException("Attempting to access " + this + " with no GL context " +
" active!");
}
}
}
static {
System.loadLibrary("filterfw");
}
private native boolean nativeAllocate(GLEnvironment env, int width, int height);
private native boolean nativeAllocateWithTexture(GLEnvironment env,
int textureId,
int width,
int height);
private native boolean nativeAllocateWithFbo(GLEnvironment env,
int fboId,
int width,
int height);
private native boolean nativeAllocateExternal(GLEnvironment env);
private native boolean nativeDeallocate();
private native boolean setNativeData(byte[] data, int offset, int length);
private native byte[] getNativeData();
private native boolean setNativeInts(int[] ints);
private native boolean setNativeFloats(float[] floats);
private native int[] getNativeInts();
private native float[] getNativeFloats();
private native boolean setNativeBitmap(Bitmap bitmap, int size);
private native boolean getNativeBitmap(Bitmap bitmap);
private native boolean setNativeViewport(int x, int y, int width, int height);
private native int getNativeTextureId();
private native int getNativeFboId();
private native boolean generateNativeMipMap();
private native boolean setNativeTextureParam(int param, int value);
private native boolean nativeResetParams();
private native boolean nativeCopyFromNative(NativeFrame frame);
private native boolean nativeCopyFromGL(GLFrame frame);
private native boolean nativeFocus();
private native boolean nativeReattachTexToFbo();
private native boolean nativeDetachTexFromFbo();
}

View File

@@ -0,0 +1,30 @@
/*
* 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.lang.annotation.*;
/**
* @hide
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface GenerateFieldPort {
String name() default "";
boolean hasDefault() default false;
}

View File

@@ -0,0 +1,30 @@
/*
* 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.lang.annotation.*;
/**
* @hide
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface GenerateFinalPort {
String name() default "";
boolean hasDefault() default false;
}

View File

@@ -0,0 +1,32 @@
/*
* 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.lang.annotation.*;
/**
* @hide
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface GenerateProgramPort {
String name();
Class type();
String variableName() default "";
boolean hasDefault() default false;
}

View File

@@ -0,0 +1,29 @@
/*
* 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.lang.annotation.*;
/**
* @hide
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface GenerateProgramPorts {
GenerateProgramPort[] value();
}

View File

@@ -0,0 +1,100 @@
/*
* 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;
/**
* @hide
*/
public abstract class GraphRunner {
protected FilterContext mFilterContext = null;
/** Interface for listeners waiting for the runner to complete. */
public interface OnRunnerDoneListener {
/** Callback method to be called when the runner completes a
* {@link #run()} call.
*
* @param result will be RESULT_FINISHED if the graph finished running
* on its own, RESULT_STOPPED if the runner was stopped by a call
* to stop(), RESULT_BLOCKED if no filters could run due to lack
* of inputs or outputs or due to scheduling policies, and
* RESULT_SLEEPING if a filter node requested sleep.
*/
public void onRunnerDone(int result);
}
public static final int RESULT_UNKNOWN = 0;
public static final int RESULT_RUNNING = 1;
public static final int RESULT_FINISHED = 2;
public static final int RESULT_SLEEPING = 3;
public static final int RESULT_BLOCKED = 4;
public static final int RESULT_STOPPED = 5;
public static final int RESULT_ERROR = 6;
public GraphRunner(FilterContext context) {
mFilterContext = context;
}
public abstract FilterGraph getGraph();
public FilterContext getContext() {
return mFilterContext;
}
/**
* Helper function for subclasses to activate the GL environment before running.
* @return true, if the GL environment was activated. Returns false, if the GL environment
* was already active.
*/
protected boolean activateGlContext() {
GLEnvironment glEnv = mFilterContext.getGLEnvironment();
if (glEnv != null && !glEnv.isActive()) {
glEnv.activate();
return true;
}
return false;
}
/**
* Helper function for subclasses to deactivate the GL environment after running.
*/
protected void deactivateGlContext() {
GLEnvironment glEnv = mFilterContext.getGLEnvironment();
if (glEnv != null) {
glEnv.deactivate();
}
}
/** Starts running the graph. Will open the filters in the graph if they are not already open. */
public abstract void run();
public abstract void setDoneCallback(OnRunnerDoneListener listener);
public abstract boolean isRunning();
/** Stops graph execution. As part of stopping, also closes the graph nodes. */
public abstract void stop();
/** Closes the filters in a graph. Can only be called if the graph is not running. */
public abstract void close();
/**
* Returns the last exception that happened during an asynchronous run. Returns null if
* there is nothing to report.
*/
public abstract Exception getError();
}

View File

@@ -0,0 +1,85 @@
/*
* 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;
/**
* @hide
*/
public abstract class InputPort extends FilterPort {
protected OutputPort mSourcePort;
public InputPort(Filter filter, String name) {
super(filter, name);
}
public void setSourcePort(OutputPort source) {
if (mSourcePort != null) {
throw new RuntimeException(this + " already connected to " + mSourcePort + "!");
}
mSourcePort = source;
}
public boolean isConnected() {
return mSourcePort != null;
}
public void open() {
super.open();
if (mSourcePort != null && !mSourcePort.isOpen()) {
mSourcePort.open();
}
}
public void close() {
if (mSourcePort != null && mSourcePort.isOpen()) {
mSourcePort.close();
}
super.close();
}
public OutputPort getSourcePort() {
return mSourcePort;
}
public Filter getSourceFilter() {
return mSourcePort == null ? null : mSourcePort.getFilter();
}
public FrameFormat getSourceFormat() {
return mSourcePort != null ? mSourcePort.getPortFormat() : getPortFormat();
}
public Object getTarget() {
return null;
}
public boolean filterMustClose() {
return !isOpen() && isBlocking() && !hasFrame();
}
public boolean isReady() {
return hasFrame() || !isBlocking();
}
public boolean acceptsFrame() {
return !hasFrame();
}
public abstract void transfer(FilterContext context);
}

View File

@@ -0,0 +1,82 @@
/*
* 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.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @hide
*/
public class KeyValueMap extends HashMap<String, Object> {
public void setKeyValues(Object... keyValues) {
if (keyValues.length % 2 != 0) {
throw new RuntimeException("Key-Value arguments passed into setKeyValues must be "
+ "an alternating list of keys and values!");
}
for (int i = 0; i < keyValues.length; i += 2) {
if (!(keyValues[i] instanceof String)) {
throw new RuntimeException("Key-value argument " + i + " must be a key of type "
+ "String, but found an object of type " + keyValues[i].getClass() + "!");
}
String key = (String)keyValues[i];
Object value = keyValues[i+1];
put(key, value);
}
}
public static KeyValueMap fromKeyValues(Object... keyValues) {
KeyValueMap result = new KeyValueMap();
result.setKeyValues(keyValues);
return result;
}
public String getString(String key) {
Object result = get(key);
return result != null ? (String)result : null;
}
public int getInt(String key) {
Object result = get(key);
return result != null ? (Integer)result : null;
}
public float getFloat(String key) {
Object result = get(key);
return result != null ? (Float)result : null;
}
@Override
public String toString() {
StringWriter writer = new StringWriter();
for (Map.Entry<String, Object> entry : entrySet()) {
String valueString;
Object value = entry.getValue();
if (value instanceof String) {
valueString = "\"" + value + "\"";
} else {
valueString = value.toString();
}
writer.write(entry.getKey() + " = " + valueString + ";\n");
}
return writer.toString();
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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 android.filterfw.core.FrameFormat;
import android.filterfw.core.KeyValueMap;
import java.util.Arrays;
/**
* @hide
*/
public class MutableFrameFormat extends FrameFormat {
public MutableFrameFormat() {
super();
}
public MutableFrameFormat(int baseType, int target) {
super(baseType, target);
}
public void setBaseType(int baseType) {
mBaseType = baseType;
mBytesPerSample = bytesPerSampleOf(baseType);
}
public void setTarget(int target) {
mTarget = target;
}
public void setBytesPerSample(int bytesPerSample) {
mBytesPerSample = bytesPerSample;
mSize = SIZE_UNKNOWN;
}
public void setDimensions(int[] dimensions) {
mDimensions = (dimensions == null) ? null : Arrays.copyOf(dimensions, dimensions.length);
mSize = SIZE_UNKNOWN;
}
public void setDimensions(int size) {
int[] dimensions = new int[1];
dimensions[0] = size;
mDimensions = dimensions;
mSize = SIZE_UNKNOWN;
}
public void setDimensions(int width, int height) {
int[] dimensions = new int[2];
dimensions[0] = width;
dimensions[1] = height;
mDimensions = dimensions;
mSize = SIZE_UNKNOWN;
}
public void setDimensions(int width, int height, int depth) {
int[] dimensions = new int[3];
dimensions[0] = width;
dimensions[1] = height;
dimensions[2] = depth;
mDimensions = dimensions;
mSize = SIZE_UNKNOWN;
}
public void setDimensionCount(int count) {
mDimensions = new int[count];
}
public void setObjectClass(Class objectClass) {
mObjectClass = objectClass;
}
public void setMetaValue(String key, Object value) {
if (mMetaData == null) {
mMetaData = new KeyValueMap();
}
mMetaData.put(key, value);
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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;
/**
* This class is simply a place-holder type used to identify calls coming
* from the native layer. This way method signatures can be selected
* that are to be accessed from the native layer only.
*
* @hide
**/
public class NativeAllocatorTag {
}

View File

@@ -0,0 +1,129 @@
/*
* 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 android.filterfw.core.Frame;
/**
* @hide
*/
public class NativeBuffer {
// These are set by the native layer
private long mDataPointer = 0;
private int mSize = 0;
private Frame mAttachedFrame;
private boolean mOwnsData = false;
private int mRefCount = 1;
public NativeBuffer() {
}
public NativeBuffer(int count) {
allocate(count * getElementSize());
mOwnsData = true;
}
public NativeBuffer mutableCopy() {
NativeBuffer result = null;
try {
Class myClass = getClass();
result = (NativeBuffer)myClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("Unable to allocate a copy of " + getClass() + "! Make " +
"sure the class has a default constructor!");
}
if (mSize > 0 && !nativeCopyTo(result)) {
throw new RuntimeException("Failed to copy NativeBuffer to mutable instance!");
}
return result;
}
public int size() {
return mSize;
}
public int count() {
return (mDataPointer != 0) ? mSize / getElementSize() : 0;
}
public int getElementSize() {
return 1;
}
public NativeBuffer retain() {
if (mAttachedFrame != null) {
mAttachedFrame.retain();
} else if (mOwnsData) {
++mRefCount;
}
return this;
}
public NativeBuffer release() {
// Decrement refcount
boolean doDealloc = false;
if (mAttachedFrame != null) {
doDealloc = (mAttachedFrame.release() == null);
} else if (mOwnsData) {
--mRefCount;
doDealloc = (mRefCount == 0);
}
// Deallocate if necessary
if (doDealloc) {
deallocate(mOwnsData);
return null;
} else {
return this;
}
}
public boolean isReadOnly() {
return (mAttachedFrame != null) ? mAttachedFrame.isReadOnly() : false;
}
static {
System.loadLibrary("filterfw");
}
void attachToFrame(Frame frame) {
// We do not auto-retain. We expect the user to call retain() if they want to hold on to
// the frame.
mAttachedFrame = frame;
}
protected void assertReadable() {
if (mDataPointer == 0 || mSize == 0
|| (mAttachedFrame != null && !mAttachedFrame.hasNativeAllocation())) {
throw new NullPointerException("Attempting to read from null data frame!");
}
}
protected void assertWritable() {
if (isReadOnly()) {
throw new RuntimeException("Attempting to modify read-only native (structured) data!");
}
}
private native boolean allocate(int size);
private native boolean deallocate(boolean ownsData);
private native boolean nativeCopyTo(NativeBuffer buffer);
}

View File

@@ -0,0 +1,265 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.FrameManager;
import android.filterfw.core.GLFrame;
import android.filterfw.core.NativeBuffer;
import android.graphics.Bitmap;
import android.util.Log;
import java.nio.ByteBuffer;
/**
* @hide
*/
public class NativeFrame extends Frame {
private int nativeFrameId = -1;
NativeFrame(FrameFormat format, FrameManager frameManager) {
super(format, frameManager);
int capacity = format.getSize();
nativeAllocate(capacity);
setReusable(capacity != 0);
}
@Override
protected synchronized void releaseNativeAllocation() {
nativeDeallocate();
nativeFrameId = -1;
}
@Override
protected synchronized boolean hasNativeAllocation() {
return nativeFrameId != -1;
}
@Override
public int getCapacity() {
return getNativeCapacity();
}
/**
* Returns the native frame's Object value.
*
* If the frame's base-type is not TYPE_OBJECT, this returns a data buffer containing the native
* data (this is equivalent to calling getData().
* If the frame is based on an object type, this type is expected to be a subclass of
* NativeBuffer. The NativeBuffer returned is only valid for as long as the frame is alive. If
* you need to hold on to the returned value, you must retain it.
*/
@Override
public Object getObjectValue() {
// If this is not a structured frame, return our data
if (getFormat().getBaseType() != FrameFormat.TYPE_OBJECT) {
return getData();
}
// Get the structure class
Class structClass = getFormat().getObjectClass();
if (structClass == null) {
throw new RuntimeException("Attempting to get object data from frame that does " +
"not specify a structure object class!");
}
// Make sure it is a NativeBuffer subclass
if (!NativeBuffer.class.isAssignableFrom(structClass)) {
throw new RuntimeException("NativeFrame object class must be a subclass of " +
"NativeBuffer!");
}
// Instantiate a new empty instance of this class
NativeBuffer structData = null;
try {
structData = (NativeBuffer)structClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("Could not instantiate new structure instance of type '" +
structClass + "'!");
}
// Wrap it around our data
if (!getNativeBuffer(structData)) {
throw new RuntimeException("Could not get the native structured data for frame!");
}
// Attach this frame to it
structData.attachToFrame(this);
return structData;
}
@Override
public void setInts(int[] ints) {
assertFrameMutable();
if (ints.length * nativeIntSize() > getFormat().getSize()) {
throw new RuntimeException(
"NativeFrame cannot hold " + ints.length + " integers. (Can only hold " +
(getFormat().getSize() / nativeIntSize()) + " integers).");
} else if (!setNativeInts(ints)) {
throw new RuntimeException("Could not set int values for native frame!");
}
}
@Override
public int[] getInts() {
return getNativeInts(getFormat().getSize());
}
@Override
public void setFloats(float[] floats) {
assertFrameMutable();
if (floats.length * nativeFloatSize() > getFormat().getSize()) {
throw new RuntimeException(
"NativeFrame cannot hold " + floats.length + " floats. (Can only hold " +
(getFormat().getSize() / nativeFloatSize()) + " floats).");
} else if (!setNativeFloats(floats)) {
throw new RuntimeException("Could not set int values for native frame!");
}
}
@Override
public float[] getFloats() {
return getNativeFloats(getFormat().getSize());
}
// TODO: This function may be a bit confusing: Is the offset the target or source offset? Maybe
// we should allow specifying both? (May be difficult for other frame types).
@Override
public void setData(ByteBuffer buffer, int offset, int length) {
assertFrameMutable();
byte[] bytes = buffer.array();
if ((length + offset) > buffer.limit()) {
throw new RuntimeException("Offset and length exceed buffer size in native setData: " +
(length + offset) + " bytes given, but only " + buffer.limit() +
" bytes available!");
} else if (getFormat().getSize() != length) {
throw new RuntimeException("Data size in setData does not match native frame size: " +
"Frame size is " + getFormat().getSize() + " bytes, but " +
length + " bytes given!");
} else if (!setNativeData(bytes, offset, length)) {
throw new RuntimeException("Could not set native frame data!");
}
}
@Override
public ByteBuffer getData() {
byte[] data = getNativeData(getFormat().getSize());
return data == null ? null : ByteBuffer.wrap(data);
}
@Override
public void setBitmap(Bitmap bitmap) {
assertFrameMutable();
if (getFormat().getNumberOfDimensions() != 2) {
throw new RuntimeException("Attempting to set Bitmap for non 2-dimensional native frame!");
} else if (getFormat().getWidth() != bitmap.getWidth() ||
getFormat().getHeight() != bitmap.getHeight()) {
throw new RuntimeException("Bitmap dimensions do not match native frame dimensions!");
} else {
Bitmap rgbaBitmap = convertBitmapToRGBA(bitmap);
int byteCount = rgbaBitmap.getByteCount();
int bps = getFormat().getBytesPerSample();
if (!setNativeBitmap(rgbaBitmap, byteCount, bps)) {
throw new RuntimeException("Could not set native frame bitmap data!");
}
}
}
@Override
public Bitmap getBitmap() {
if (getFormat().getNumberOfDimensions() != 2) {
throw new RuntimeException("Attempting to get Bitmap for non 2-dimensional native frame!");
}
Bitmap result = Bitmap.createBitmap(getFormat().getWidth(),
getFormat().getHeight(),
Bitmap.Config.ARGB_8888);
int byteCount = result.getByteCount();
int bps = getFormat().getBytesPerSample();
if (!getNativeBitmap(result, byteCount, bps)) {
throw new RuntimeException("Could not get bitmap data from native frame!");
}
return result;
}
@Override
public void setDataFromFrame(Frame frame) {
// Make sure frame fits
if (getFormat().getSize() < frame.getFormat().getSize()) {
throw new RuntimeException(
"Attempting to assign frame of size " + frame.getFormat().getSize() + " to " +
"smaller native frame of size " + getFormat().getSize() + "!");
}
// Invoke optimized implementations if possible
if (frame instanceof NativeFrame) {
nativeCopyFromNative((NativeFrame)frame);
} else if (frame instanceof GLFrame) {
nativeCopyFromGL((GLFrame)frame);
} else if (frame instanceof SimpleFrame) {
setObjectValue(frame.getObjectValue());
} else {
super.setDataFromFrame(frame);
}
}
@Override
public String toString() {
return "NativeFrame id: " + nativeFrameId + " (" + getFormat() + ") of size "
+ getCapacity();
}
static {
System.loadLibrary("filterfw");
}
private native boolean nativeAllocate(int capacity);
private native boolean nativeDeallocate();
private native int getNativeCapacity();
private static native int nativeIntSize();
private static native int nativeFloatSize();
private native boolean setNativeData(byte[] data, int offset, int length);
private native byte[] getNativeData(int byteCount);
private native boolean getNativeBuffer(NativeBuffer buffer);
private native boolean setNativeInts(int[] ints);
private native boolean setNativeFloats(float[] floats);
private native int[] getNativeInts(int byteCount);
private native float[] getNativeFloats(int byteCount);
private native boolean setNativeBitmap(Bitmap bitmap, int size, int bytesPerSample);
private native boolean getNativeBitmap(Bitmap bitmap, int size, int bytesPerSample);
private native boolean nativeCopyFromNative(NativeFrame frame);
private native boolean nativeCopyFromGL(GLFrame frame);
}

View File

@@ -0,0 +1,176 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.Program;
/**
* @hide
*/
public class NativeProgram extends Program {
private int nativeProgramId;
private boolean mHasInitFunction = false;
private boolean mHasTeardownFunction = false;
private boolean mHasSetValueFunction = false;
private boolean mHasGetValueFunction = false;
private boolean mHasResetFunction = false;
private boolean mTornDown = false;
public NativeProgram(String nativeLibName, String nativeFunctionPrefix) {
// Allocate the native instance
allocate();
// Open the native library
String fullLibName = "lib" + nativeLibName + ".so";
if (!openNativeLibrary(fullLibName)) {
throw new RuntimeException("Could not find native library named '" + fullLibName + "' " +
"required for native program!");
}
// Bind the native functions
String processFuncName = nativeFunctionPrefix + "_process";
if (!bindProcessFunction(processFuncName)) {
throw new RuntimeException("Could not find native program function name " +
processFuncName + " in library " + fullLibName + "! " +
"This function is required!");
}
String initFuncName = nativeFunctionPrefix + "_init";
mHasInitFunction = bindInitFunction(initFuncName);
String teardownFuncName = nativeFunctionPrefix + "_teardown";
mHasTeardownFunction = bindTeardownFunction(teardownFuncName);
String setValueFuncName = nativeFunctionPrefix + "_setvalue";
mHasSetValueFunction = bindSetValueFunction(setValueFuncName);
String getValueFuncName = nativeFunctionPrefix + "_getvalue";
mHasGetValueFunction = bindGetValueFunction(getValueFuncName);
String resetFuncName = nativeFunctionPrefix + "_reset";
mHasResetFunction = bindResetFunction(resetFuncName);
// Initialize the native code
if (mHasInitFunction && !callNativeInit()) {
throw new RuntimeException("Could not initialize NativeProgram!");
}
}
public void tearDown() {
if (mTornDown) return;
if (mHasTeardownFunction && !callNativeTeardown()) {
throw new RuntimeException("Could not tear down NativeProgram!");
}
deallocate();
mTornDown = true;
}
@Override
public void reset() {
if (mHasResetFunction && !callNativeReset()) {
throw new RuntimeException("Could not reset NativeProgram!");
}
}
@Override
protected void finalize() throws Throwable {
tearDown();
}
@Override
public void process(Frame[] inputs, Frame output) {
if (mTornDown) {
throw new RuntimeException("NativeProgram already torn down!");
}
NativeFrame[] nativeInputs = new NativeFrame[inputs.length];
for (int i = 0; i < inputs.length; ++i) {
if (inputs[i] == null || inputs[i] instanceof NativeFrame) {
nativeInputs[i] = (NativeFrame)inputs[i];
} else {
throw new RuntimeException("NativeProgram got non-native frame as input "+ i +"!");
}
}
// Get the native output frame
NativeFrame nativeOutput = null;
if (output == null || output instanceof NativeFrame) {
nativeOutput = (NativeFrame)output;
} else {
throw new RuntimeException("NativeProgram got non-native output frame!");
}
// Process!
if (!callNativeProcess(nativeInputs, nativeOutput)) {
throw new RuntimeException("Calling native process() caused error!");
}
}
@Override
public void setHostValue(String variableName, Object value) {
if (mTornDown) {
throw new RuntimeException("NativeProgram already torn down!");
}
if (!mHasSetValueFunction) {
throw new RuntimeException("Attempting to set native variable, but native code does not " +
"define native setvalue function!");
}
if (!callNativeSetValue(variableName, value.toString())) {
throw new RuntimeException("Error setting native value for variable '" + variableName + "'!");
}
}
@Override
public Object getHostValue(String variableName) {
if (mTornDown) {
throw new RuntimeException("NativeProgram already torn down!");
}
if (!mHasGetValueFunction) {
throw new RuntimeException("Attempting to get native variable, but native code does not " +
"define native getvalue function!");
}
return callNativeGetValue(variableName);
}
static {
System.loadLibrary("filterfw");
}
private native boolean allocate();
private native boolean deallocate();
private native boolean nativeInit();
private native boolean openNativeLibrary(String libName);
private native boolean bindInitFunction(String funcName);
private native boolean bindSetValueFunction(String funcName);
private native boolean bindGetValueFunction(String funcName);
private native boolean bindProcessFunction(String funcName);
private native boolean bindResetFunction(String funcName);
private native boolean bindTeardownFunction(String funcName);
private native boolean callNativeInit();
private native boolean callNativeSetValue(String key, String value);
private native String callNativeGetValue(String key);
private native boolean callNativeProcess(NativeFrame[] inputs, NativeFrame output);
private native boolean callNativeReset();
private native boolean callNativeTeardown();
}

View File

@@ -0,0 +1,78 @@
/*
* 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 android.filterfw.core.Filter;
import android.filterfw.core.Scheduler;
import android.filterfw.core.RoundRobinScheduler;
import android.util.Log;
import java.util.HashMap;
/**
* This OneShotScheduler only schedules source filters at most once. All other
* filters will be scheduled, and possibly repeatedly, until there is no filter
* that can be scheduled.
*
* @hide
*/
public class OneShotScheduler extends RoundRobinScheduler {
private HashMap <String, Integer> scheduled;
private final boolean mLogVerbose;
private static final String TAG = "OneShotScheduler";
public OneShotScheduler(FilterGraph graph) {
super(graph);
scheduled = new HashMap<String, Integer>();
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
}
@Override
public void reset() {
super.reset();
scheduled.clear();
}
@Override
public Filter scheduleNextNode() {
Filter first = null;
// return the first filter that is not scheduled before.
while (true) {
Filter filter = super.scheduleNextNode();
if (filter == null) {
if (mLogVerbose) Log.v(TAG, "No filters available to run.");
return null;
}
if (!scheduled.containsKey(filter.getName())) {
if (filter.getNumberOfConnectedInputs() == 0)
scheduled.put(filter.getName(),1);
if (mLogVerbose) Log.v(TAG, "Scheduling filter \"" + filter.getName() + "\" of type " + filter.getFilterClassName());
return filter;
}
// if loop back, nothing available
if (first == filter) {
break;
}
// save the first scheduled one
if (first == null) first = filter;
}
if (mLogVerbose) Log.v(TAG, "One pass through graph completed.");
return null;
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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;
/**
* @hide
*/
public class OutputPort extends FilterPort {
protected InputPort mTargetPort;
protected InputPort mBasePort;
public OutputPort(Filter filter, String name) {
super(filter, name);
}
public void connectTo(InputPort target) {
if (mTargetPort != null) {
throw new RuntimeException(this + " already connected to " + mTargetPort + "!");
}
mTargetPort = target;
mTargetPort.setSourcePort(this);
}
public boolean isConnected() {
return mTargetPort != null;
}
public void open() {
super.open();
if (mTargetPort != null && !mTargetPort.isOpen()) {
mTargetPort.open();
}
}
public void close() {
super.close();
if (mTargetPort != null && mTargetPort.isOpen()) {
mTargetPort.close();
}
}
public InputPort getTargetPort() {
return mTargetPort;
}
public Filter getTargetFilter() {
return mTargetPort == null ? null : mTargetPort.getFilter();
}
public void setBasePort(InputPort basePort) {
mBasePort = basePort;
}
public InputPort getBasePort() {
return mBasePort;
}
public boolean filterMustClose() {
return !isOpen() && isBlocking();
}
public boolean isReady() {
return (isOpen() && mTargetPort.acceptsFrame()) || !isBlocking();
}
@Override
public void clear() {
if (mTargetPort != null) {
mTargetPort.clear();
}
}
@Override
public void pushFrame(Frame frame) {
if (mTargetPort == null) {
throw new RuntimeException(
"Attempting to push frame on unconnected port: " + this + "!");
}
mTargetPort.pushFrame(frame);
}
@Override
public void setFrame(Frame frame) {
assertPortIsOpen();
if (mTargetPort == null) {
throw new RuntimeException(
"Attempting to set frame on unconnected port: " + this + "!");
}
mTargetPort.setFrame(frame);
}
@Override
public Frame pullFrame() {
throw new RuntimeException("Cannot pull frame on " + this + "!");
}
@Override
public boolean hasFrame() {
return mTargetPort == null ? false : mTargetPort.hasFrame();
}
@Override
public String toString() {
return "output " + super.toString();
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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 android.filterfw.core.Frame;
/**
* @hide
*/
public abstract class Program {
public abstract void process(Frame[] inputs, Frame output);
public void process(Frame input, Frame output) {
Frame[] inputs = new Frame[1];
inputs[0] = input;
process(inputs, output);
}
public abstract void setHostValue(String variableName, Object value);
public abstract Object getHostValue(String variableName);
public void reset() {
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.lang.reflect.Field;
/**
* @hide
*/
public class ProgramPort extends FieldPort {
protected String mVarName;
public ProgramPort(Filter filter,
String name,
String varName,
Field field,
boolean hasDefault) {
super(filter, name, field, hasDefault);
mVarName = varName;
}
@Override
public String toString() {
return "Program " + super.toString();
}
@Override
public synchronized void transfer(FilterContext context) {
if (mValueWaiting) {
try {
Object fieldValue = mField.get(mFilter);
if (fieldValue != null) {
Program program = (Program)fieldValue;
program.setHostValue(mVarName, mValue);
mValueWaiting = false;
}
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Access to program field '" + mField.getName() + "' was denied!");
} catch (ClassCastException e) {
throw new RuntimeException("Non Program field '" + mField.getName()
+ "' annotated with ProgramParameter!");
}
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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;
/**
* @hide
*/
public class ProgramVariable {
private Program mProgram;
private String mVarName;
public ProgramVariable(Program program, String varName) {
mProgram = program;
mVarName = varName;
}
public Program getProgram() {
return mProgram;
}
public String getVariableName() {
return mVarName;
}
public void setValue(Object value) {
if (mProgram == null) {
throw new RuntimeException("Attempting to set program variable '" + mVarName
+ "' but the program is null!");
}
mProgram.setHostValue(mVarName, value);
}
public Object getValue() {
if (mProgram == null) {
throw new RuntimeException("Attempting to get program variable '" + mVarName
+ "' but the program is null!");
}
return mProgram.getHostValue(mVarName);
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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;
/**
* @hide
*/
public class ProtocolException extends RuntimeException {
public ProtocolException() {
super();
}
public ProtocolException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.Random;
import java.util.Vector;
import android.filterfw.core.Filter;
import android.filterfw.core.Scheduler;
/**
* @hide
*/
public class RandomScheduler extends Scheduler {
private Random mRand = new Random();
public RandomScheduler(FilterGraph graph) {
super(graph);
}
@Override
public void reset() {
}
@Override
public Filter scheduleNextNode() {
Vector<Filter> candidates = new Vector<Filter>();
for (Filter filter : getGraph().getFilters()) {
if (filter.canProcess())
candidates.add(filter);
}
if (candidates.size() > 0) {
int r = mRand.nextInt(candidates.size());
return candidates.elementAt(r);
}
return null;
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.Set;
import android.filterfw.core.Filter;
import android.filterfw.core.Scheduler;
/**
* @hide
*/
public class RoundRobinScheduler extends Scheduler {
private int mLastPos = -1;
public RoundRobinScheduler(FilterGraph graph) {
super(graph);
}
@Override
public void reset() {
mLastPos = -1;
}
@Override
public Filter scheduleNextNode() {
Set<Filter> all_filters = getGraph().getFilters();
if (mLastPos >= all_filters.size()) mLastPos = -1;
int pos = 0;
Filter first = null;
int firstNdx = -1;
for (Filter filter : all_filters) {
if (filter.canProcess()) {
if (pos <= mLastPos) {
if (first == null) {
// store the first available filter
first = filter;
firstNdx = pos;
}
} else {
// return the next available filter since last
mLastPos = pos;
return filter;
}
}
pos ++;
}
// going around from the beginning
if (first != null ) {
mLastPos = firstNdx;
return first;
}
// if there is nothing to be scheduled, still keep the previous
// position.
return null;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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 android.filterfw.core.Filter;
import android.filterfw.core.FilterGraph;
/**
* @hide
*/
public abstract class Scheduler {
// All methods are core internal methods as Scheduler internals are only used by the GraphRunner.
private FilterGraph mGraph;
Scheduler(FilterGraph graph) {
mGraph = graph;
}
FilterGraph getGraph() {
return mGraph;
}
abstract void reset();
abstract Filter scheduleNextNode();
boolean finished() {
// TODO: Check that the state of all nodes is FINISHED.
return true;
}
}

View File

@@ -0,0 +1,287 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.FrameManager;
import android.filterfw.core.NativeBuffer;
import android.filterfw.format.ObjectFormat;
import android.graphics.Bitmap;
import java.io.InputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.OutputStream;
import java.io.StreamCorruptedException;
import java.lang.reflect.Constructor;
import java.nio.ByteBuffer;
/**
* A frame that serializes any assigned values. Such a frame is used when passing data objects
* between threads.
*
* @hide
*/
public class SerializedFrame extends Frame {
/**
* The initial capacity of the serialized data stream.
*/
private final static int INITIAL_CAPACITY = 64;
/**
* The internal data streams.
*/
private DirectByteOutputStream mByteOutputStream;
private ObjectOutputStream mObjectOut;
/**
* An unsynchronized output stream that writes data to an accessible byte array. Callers are
* responsible for synchronization. This is more efficient than a ByteArrayOutputStream, as
* there are no array copies or synchronization involved to read back written data.
*/
private class DirectByteOutputStream extends OutputStream {
private byte[] mBuffer = null;
private int mOffset = 0;
private int mDataOffset = 0;
public DirectByteOutputStream(int size) {
mBuffer = new byte[size];
}
private final void ensureFit(int bytesToWrite) {
if (mOffset + bytesToWrite > mBuffer.length) {
byte[] oldBuffer = mBuffer;
mBuffer = new byte[Math.max(mOffset + bytesToWrite, mBuffer.length * 2)];
System.arraycopy(oldBuffer, 0, mBuffer, 0, mOffset);
oldBuffer = null;
}
}
public final void markHeaderEnd() {
mDataOffset = mOffset;
}
public final int getSize() {
return mOffset;
}
public byte[] getByteArray() {
return mBuffer;
}
@Override
public final void write(byte b[]) {
write(b, 0, b.length);
}
@Override
public final void write(byte b[], int off, int len) {
ensureFit(len);
System.arraycopy(b, off, mBuffer, mOffset, len);
mOffset += len;
}
@Override
public final void write(int b) {
ensureFit(1);
mBuffer[mOffset++] = (byte)b;
}
public final void reset() {
mOffset = mDataOffset;
}
public final DirectByteInputStream getInputStream() {
return new DirectByteInputStream(mBuffer, mOffset);
}
}
/**
* An unsynchronized input stream that reads data directly from a provided byte array. Callers
* are responsible for synchronization and ensuring that the byte buffer is valid.
*/
private class DirectByteInputStream extends InputStream {
private byte[] mBuffer;
private int mPos = 0;
private int mSize;
public DirectByteInputStream(byte[] buffer, int size) {
mBuffer = buffer;
mSize = size;
}
@Override
public final int available() {
return mSize - mPos;
}
@Override
public final int read() {
return (mPos < mSize) ? (mBuffer[mPos++] & 0xFF) : -1;
}
@Override
public final int read(byte[] b, int off, int len) {
if (mPos >= mSize) {
return -1;
}
if ((mPos + len) > mSize) {
len = mSize - mPos;
}
System.arraycopy(mBuffer, mPos, b, off, len);
mPos += len;
return len;
}
@Override
public final long skip(long n) {
if ((mPos + n) > mSize) {
n = mSize - mPos;
}
if (n < 0) {
return 0;
}
mPos += n;
return n;
}
}
SerializedFrame(FrameFormat format, FrameManager frameManager) {
super(format, frameManager);
setReusable(false);
// Setup streams
try {
mByteOutputStream = new DirectByteOutputStream(INITIAL_CAPACITY);
mObjectOut = new ObjectOutputStream(mByteOutputStream);
mByteOutputStream.markHeaderEnd();
} catch (IOException e) {
throw new RuntimeException("Could not create serialization streams for "
+ "SerializedFrame!", e);
}
}
static SerializedFrame wrapObject(Object object, FrameManager frameManager) {
FrameFormat format = ObjectFormat.fromObject(object, FrameFormat.TARGET_SIMPLE);
SerializedFrame result = new SerializedFrame(format, frameManager);
result.setObjectValue(object);
return result;
}
@Override
protected boolean hasNativeAllocation() {
return false;
}
@Override
protected void releaseNativeAllocation() {
}
@Override
public Object getObjectValue() {
return deserializeObjectValue();
}
@Override
public void setInts(int[] ints) {
assertFrameMutable();
setGenericObjectValue(ints);
}
@Override
public int[] getInts() {
Object result = deserializeObjectValue();
return (result instanceof int[]) ? (int[])result : null;
}
@Override
public void setFloats(float[] floats) {
assertFrameMutable();
setGenericObjectValue(floats);
}
@Override
public float[] getFloats() {
Object result = deserializeObjectValue();
return (result instanceof float[]) ? (float[])result : null;
}
@Override
public void setData(ByteBuffer buffer, int offset, int length) {
assertFrameMutable();
setGenericObjectValue(ByteBuffer.wrap(buffer.array(), offset, length));
}
@Override
public ByteBuffer getData() {
Object result = deserializeObjectValue();
return (result instanceof ByteBuffer) ? (ByteBuffer)result : null;
}
@Override
public void setBitmap(Bitmap bitmap) {
assertFrameMutable();
setGenericObjectValue(bitmap);
}
@Override
public Bitmap getBitmap() {
Object result = deserializeObjectValue();
return (result instanceof Bitmap) ? (Bitmap)result : null;
}
@Override
protected void setGenericObjectValue(Object object) {
serializeObjectValue(object);
}
private final void serializeObjectValue(Object object) {
try {
mByteOutputStream.reset();
mObjectOut.writeObject(object);
mObjectOut.flush();
mObjectOut.close();
} catch (IOException e) {
throw new RuntimeException("Could not serialize object " + object + " in "
+ this + "!", e);
}
}
private final Object deserializeObjectValue() {
try {
InputStream inputStream = mByteOutputStream.getInputStream();
ObjectInputStream objectStream = new ObjectInputStream(inputStream);
return objectStream.readObject();
} catch (IOException e) {
throw new RuntimeException("Could not deserialize object in " + this + "!", e);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to deserialize object of unknown class in "
+ this + "!", e);
}
}
@Override
public String toString() {
return "SerializedFrame (" + getFormat() + ")";
}
}

View File

@@ -0,0 +1,301 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.NativeAllocatorTag;
import android.filterfw.core.Program;
import android.filterfw.core.StopWatchMap;
import android.filterfw.core.VertexFrame;
import android.filterfw.geometry.Quad;
import android.opengl.GLES20;
/**
* @hide
*/
public class ShaderProgram extends Program {
private int shaderProgramId;
private int mMaxTileSize = 0;
// Keep a reference to the GL environment, so that it does not get deallocated while there
// are still programs living in it.
private GLEnvironment mGLEnvironment;
private StopWatchMap mTimer = null;
private void setTimer() {
mTimer = new StopWatchMap();
}
// Used from native layer for creating empty wrapper only!
private ShaderProgram() {
}
private ShaderProgram(NativeAllocatorTag tag) {
}
public ShaderProgram(FilterContext context, String fragmentShader) {
mGLEnvironment = getGLEnvironment(context);
allocate(mGLEnvironment, null, fragmentShader);
if (!compileAndLink()) {
throw new RuntimeException("Could not compile and link shader!");
}
this.setTimer();
}
public ShaderProgram(FilterContext context, String vertexShader, String fragmentShader) {
mGLEnvironment = getGLEnvironment(context);
allocate(mGLEnvironment, vertexShader, fragmentShader);
if (!compileAndLink()) {
throw new RuntimeException("Could not compile and link shader!");
}
this.setTimer();
}
public static ShaderProgram createIdentity(FilterContext context) {
ShaderProgram program = nativeCreateIdentity(getGLEnvironment(context));
program.setTimer();
return program;
}
@Override
protected void finalize() throws Throwable {
deallocate();
}
public GLEnvironment getGLEnvironment() {
return mGLEnvironment;
}
@Override
public void process(Frame[] inputs, Frame output) {
if (mTimer.LOG_MFF_RUNNING_TIMES) {
mTimer.start("glFinish");
GLES20.glFinish();
mTimer.stop("glFinish");
}
// Get the GL input frames
// TODO: We do the same in the NativeProgram... can we find a better way?!
GLFrame[] glInputs = new GLFrame[inputs.length];
for (int i = 0; i < inputs.length; ++i) {
if (inputs[i] instanceof GLFrame) {
glInputs[i] = (GLFrame)inputs[i];
} else {
throw new RuntimeException("ShaderProgram got non-GL frame as input " + i + "!");
}
}
// Get the GL output frame
GLFrame glOutput = null;
if (output instanceof GLFrame) {
glOutput = (GLFrame)output;
} else {
throw new RuntimeException("ShaderProgram got non-GL output frame!");
}
// Adjust tiles to meet maximum tile size requirement
if (mMaxTileSize > 0) {
int xTiles = (output.getFormat().getWidth() + mMaxTileSize - 1) / mMaxTileSize;
int yTiles = (output.getFormat().getHeight() + mMaxTileSize - 1) / mMaxTileSize;
setShaderTileCounts(xTiles, yTiles);
}
// Process!
if (!shaderProcess(glInputs, glOutput)) {
throw new RuntimeException("Error executing ShaderProgram!");
}
if (mTimer.LOG_MFF_RUNNING_TIMES) {
GLES20.glFinish();
}
}
@Override
public void setHostValue(String variableName, Object value) {
if (!setUniformValue(variableName, value)) {
throw new RuntimeException("Error setting uniform value for variable '" +
variableName + "'!");
}
}
@Override
public Object getHostValue(String variableName) {
return getUniformValue(variableName);
}
public void setAttributeValues(String attributeName, float[] data, int componentCount) {
if (!setShaderAttributeValues(attributeName, data, componentCount)) {
throw new RuntimeException("Error setting attribute value for attribute '" +
attributeName + "'!");
}
}
public void setAttributeValues(String attributeName,
VertexFrame vertexData,
int type,
int componentCount,
int strideInBytes,
int offsetInBytes,
boolean normalize) {
if (!setShaderAttributeVertexFrame(attributeName,
vertexData,
type,
componentCount,
strideInBytes,
offsetInBytes,
normalize)) {
throw new RuntimeException("Error setting attribute value for attribute '" +
attributeName + "'!");
}
}
public void setSourceRegion(Quad region) {
setSourceRegion(region.p0.x, region.p0.y,
region.p1.x, region.p1.y,
region.p2.x, region.p2.y,
region.p3.x, region.p3.y);
}
public void setTargetRegion(Quad region) {
setTargetRegion(region.p0.x, region.p0.y,
region.p1.x, region.p1.y,
region.p2.x, region.p2.y,
region.p3.x, region.p3.y);
}
public void setSourceRect(float x, float y, float width, float height) {
setSourceRegion(x, y, x + width, y, x, y + height, x + width, y + height);
}
public void setTargetRect(float x, float y, float width, float height) {
setTargetRegion(x, y, x + width, y, x, y + height, x + width, y + height);
}
public void setClearsOutput(boolean clears) {
if (!setShaderClearsOutput(clears)) {
throw new RuntimeException("Could not set clears-output flag to " + clears + "!");
}
}
public void setClearColor(float r, float g, float b) {
if (!setShaderClearColor(r, g, b)) {
throw new RuntimeException("Could not set clear color to " + r + "," + g + "," + b + "!");
}
}
public void setBlendEnabled(boolean enable) {
if (!setShaderBlendEnabled(enable)) {
throw new RuntimeException("Could not set Blending " + enable + "!");
}
}
public void setBlendFunc(int sfactor, int dfactor) {
if (!setShaderBlendFunc(sfactor, dfactor)) {
throw new RuntimeException("Could not set BlendFunc " + sfactor +","+ dfactor + "!");
}
}
public void setDrawMode(int drawMode) {
if (!setShaderDrawMode(drawMode)) {
throw new RuntimeException("Could not set GL draw-mode to " + drawMode + "!");
}
}
public void setVertexCount(int count) {
if (!setShaderVertexCount(count)) {
throw new RuntimeException("Could not set GL vertex count to " + count + "!");
}
}
public void setMaximumTileSize(int size) {
mMaxTileSize = size;
}
public void beginDrawing() {
if (!beginShaderDrawing()) {
throw new RuntimeException("Could not prepare shader-program for drawing!");
}
}
private static GLEnvironment getGLEnvironment(FilterContext context) {
GLEnvironment result = context != null ? context.getGLEnvironment() : null;
if (result == null) {
throw new NullPointerException("Attempting to create ShaderProgram with no GL "
+ "environment in place!");
}
return result;
}
static {
System.loadLibrary("filterfw");
}
private native boolean allocate(GLEnvironment glEnv,
String vertexShader,
String fragmentShader);
private native boolean deallocate();
private native boolean compileAndLink();
private native boolean shaderProcess(GLFrame[] inputs, GLFrame output);
private native boolean setUniformValue(String name, Object value);
private native Object getUniformValue(String name);
public native boolean setSourceRegion(float x0, float y0, float x1, float y1,
float x2, float y2, float x3, float y3);
private native boolean setTargetRegion(float x0, float y0, float x1, float y1,
float x2, float y2, float x3, float y3);
private static native ShaderProgram nativeCreateIdentity(GLEnvironment glEnv);
private native boolean setShaderClearsOutput(boolean clears);
private native boolean setShaderBlendEnabled(boolean enable);
private native boolean setShaderBlendFunc(int sfactor, int dfactor);
private native boolean setShaderClearColor(float r, float g, float b);
private native boolean setShaderDrawMode(int drawMode);
private native boolean setShaderTileCounts(int xCount, int yCount);
private native boolean setShaderVertexCount(int vertexCount);
private native boolean beginShaderDrawing();
private native boolean setShaderAttributeValues(String attributeName,
float[] data,
int componentCount);
private native boolean setShaderAttributeVertexFrame(String attributeName,
VertexFrame vertexData,
int type,
int componentCount,
int strideInBytes,
int offsetInBytes,
boolean normalize);
}

View File

@@ -0,0 +1,161 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.FrameManager;
import android.filterfw.core.NativeBuffer;
import android.filterfw.format.ObjectFormat;
import android.graphics.Bitmap;
import java.lang.reflect.Constructor;
import java.nio.ByteBuffer;
/**
* @hide
*/
public class SimpleFrame extends Frame {
private Object mObject;
SimpleFrame(FrameFormat format, FrameManager frameManager) {
super(format, frameManager);
initWithFormat(format);
setReusable(false);
}
static SimpleFrame wrapObject(Object object, FrameManager frameManager) {
FrameFormat format = ObjectFormat.fromObject(object, FrameFormat.TARGET_SIMPLE);
SimpleFrame result = new SimpleFrame(format, frameManager);
result.setObjectValue(object);
return result;
}
private void initWithFormat(FrameFormat format) {
final int count = format.getLength();
final int baseType = format.getBaseType();
switch (baseType) {
case FrameFormat.TYPE_BYTE:
mObject = new byte[count];
break;
case FrameFormat.TYPE_INT16:
mObject = new short[count];
break;
case FrameFormat.TYPE_INT32:
mObject = new int[count];
break;
case FrameFormat.TYPE_FLOAT:
mObject = new float[count];
break;
case FrameFormat.TYPE_DOUBLE:
mObject = new double[count];
break;
default:
mObject = null;
break;
}
}
@Override
protected boolean hasNativeAllocation() {
return false;
}
@Override
protected void releaseNativeAllocation() {
}
@Override
public Object getObjectValue() {
return mObject;
}
@Override
public void setInts(int[] ints) {
assertFrameMutable();
setGenericObjectValue(ints);
}
@Override
public int[] getInts() {
return (mObject instanceof int[]) ? (int[])mObject : null;
}
@Override
public void setFloats(float[] floats) {
assertFrameMutable();
setGenericObjectValue(floats);
}
@Override
public float[] getFloats() {
return (mObject instanceof float[]) ? (float[])mObject : null;
}
@Override
public void setData(ByteBuffer buffer, int offset, int length) {
assertFrameMutable();
setGenericObjectValue(ByteBuffer.wrap(buffer.array(), offset, length));
}
@Override
public ByteBuffer getData() {
return (mObject instanceof ByteBuffer) ? (ByteBuffer)mObject : null;
}
@Override
public void setBitmap(Bitmap bitmap) {
assertFrameMutable();
setGenericObjectValue(bitmap);
}
@Override
public Bitmap getBitmap() {
return (mObject instanceof Bitmap) ? (Bitmap)mObject : null;
}
private void setFormatObjectClass(Class objectClass) {
MutableFrameFormat format = getFormat().mutableCopy();
format.setObjectClass(objectClass);
setFormat(format);
}
@Override
protected void setGenericObjectValue(Object object) {
// Update the FrameFormat class
// TODO: Take this out! FrameFormats should not be modified and convenience formats used
// instead!
FrameFormat format = getFormat();
if (format.getObjectClass() == null) {
setFormatObjectClass(object.getClass());
} else if (!format.getObjectClass().isAssignableFrom(object.getClass())) {
throw new RuntimeException(
"Attempting to set object value of type '" + object.getClass() + "' on " +
"SimpleFrame of type '" + format.getObjectClass() + "'!");
}
// Set the object value
mObject = object;
}
@Override
public String toString() {
return "SimpleFrame (" + getFormat() + ")";
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.FrameManager;
import android.filterfw.core.GLFrame;
import android.filterfw.core.NativeFrame;
import android.filterfw.core.SimpleFrame;
import android.filterfw.core.VertexFrame;
/**
* @hide
*/
public class SimpleFrameManager extends FrameManager {
public SimpleFrameManager() {
}
@Override
public Frame newFrame(FrameFormat format) {
return createNewFrame(format);
}
@Override
public Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId) {
Frame result = null;
switch(format.getTarget()) {
case FrameFormat.TARGET_GPU: {
GLFrame glFrame = new GLFrame(format, this, bindingType, bindingId);
glFrame.init(getGLEnvironment());
result = glFrame;
break;
}
default:
throw new RuntimeException("Attached frames are not supported for target type: "
+ FrameFormat.targetToString(format.getTarget()) + "!");
}
return result;
}
private Frame createNewFrame(FrameFormat format) {
Frame result = null;
switch(format.getTarget()) {
case FrameFormat.TARGET_SIMPLE:
result = new SimpleFrame(format, this);
break;
case FrameFormat.TARGET_NATIVE:
result = new NativeFrame(format, this);
break;
case FrameFormat.TARGET_GPU: {
GLFrame glFrame = new GLFrame(format, this);
glFrame.init(getGLEnvironment());
result = glFrame;
break;
}
case FrameFormat.TARGET_VERTEXBUFFER: {
result = new VertexFrame(format, this);
break;
}
default:
throw new RuntimeException("Unsupported frame target type: " +
FrameFormat.targetToString(format.getTarget()) + "!");
}
return result;
}
@Override
public Frame retainFrame(Frame frame) {
frame.incRefCount();
return frame;
}
@Override
public Frame releaseFrame(Frame frame) {
int refCount = frame.decRefCount();
if (refCount == 0 && frame.hasNativeAllocation()) {
frame.releaseNativeAllocation();
return null;
} else if (refCount < 0) {
throw new RuntimeException("Frame reference count dropped below 0!");
}
return frame;
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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 android.filterfw.core.Filter;
import android.filterfw.core.Scheduler;
/**
* @hide
*/
public class SimpleScheduler extends Scheduler {
public SimpleScheduler(FilterGraph graph) {
super(graph);
}
@Override
public void reset() {
}
@Override
public Filter scheduleNextNode() {
for (Filter filter : getGraph().getFilters()) {
if (filter.canProcess())
return filter;
}
return null;
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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 android.os.SystemClock;
import android.util.Log;
import java.util.HashMap;
/**
* @hide
*/
class StopWatch {
private int STOP_WATCH_LOGGING_PERIOD = 200;
private String TAG = "MFF";
private String mName;
private long mStartTime;
private long mTotalTime;
private int mNumCalls;
public StopWatch(String name) {
mName = name;
mStartTime = -1;
mTotalTime = 0;
mNumCalls = 0;
}
public void start() {
if (mStartTime != -1) {
throw new RuntimeException(
"Calling start with StopWatch already running");
}
mStartTime = SystemClock.elapsedRealtime();
}
public void stop() {
if (mStartTime == -1) {
throw new RuntimeException(
"Calling stop with StopWatch already stopped");
}
long stopTime = SystemClock.elapsedRealtime();
mTotalTime += stopTime - mStartTime;
++mNumCalls;
mStartTime = -1;
if (mNumCalls % STOP_WATCH_LOGGING_PERIOD == 0) {
Log.i(TAG, "AVG ms/call " + mName + ": " +
String.format("%.1f", mTotalTime * 1.0f / mNumCalls));
mTotalTime = 0;
mNumCalls = 0;
}
}
}
public class StopWatchMap {
public boolean LOG_MFF_RUNNING_TIMES = false;
private HashMap<String, StopWatch> mStopWatches = null;
public StopWatchMap() {
mStopWatches = new HashMap<String, StopWatch>();
}
public void start(String stopWatchName) {
if (!LOG_MFF_RUNNING_TIMES) {
return;
}
if (!mStopWatches.containsKey(stopWatchName)) {
mStopWatches.put(stopWatchName, new StopWatch(stopWatchName));
}
mStopWatches.get(stopWatchName).start();
}
public void stop(String stopWatchName) {
if (!LOG_MFF_RUNNING_TIMES) {
return;
}
if (!mStopWatches.containsKey(stopWatchName)) {
throw new RuntimeException(
"Calling stop with unknown stopWatchName: " + stopWatchName);
}
mStopWatches.get(stopWatchName).stop();
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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;
/**
* @hide
*/
public class StreamPort extends InputPort {
private Frame mFrame;
private boolean mPersistent;
public StreamPort(Filter filter, String name) {
super(filter, name);
}
@Override
public void clear() {
if (mFrame != null) {
mFrame.release();
mFrame = null;
}
}
@Override
public void setFrame(Frame frame) {
assignFrame(frame, true);
}
@Override
public void pushFrame(Frame frame) {
assignFrame(frame, false);
}
protected synchronized void assignFrame(Frame frame, boolean persistent) {
assertPortIsOpen();
checkFrameType(frame, persistent);
if (persistent) {
if (mFrame != null) {
mFrame.release();
}
} else if (mFrame != null) {
throw new RuntimeException(
"Attempting to push more than one frame on port: " + this + "!");
}
mFrame = frame.retain();
mFrame.markReadOnly();
mPersistent = persistent;
}
@Override
public synchronized Frame pullFrame() {
// Make sure we have a frame
if (mFrame == null) {
throw new RuntimeException("No frame available to pull on port: " + this + "!");
}
// Return a retained result
Frame result = mFrame;
if (mPersistent) {
mFrame.retain();
} else {
mFrame = null;
}
return result;
}
@Override
public synchronized boolean hasFrame() {
return mFrame != null;
}
@Override
public String toString() {
return "input " + super.toString();
}
@Override
public synchronized void transfer(FilterContext context) {
if (mFrame != null) {
checkFrameManager(mFrame, context);
}
}
}

View File

@@ -0,0 +1,227 @@
/*
* 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 android.os.ConditionVariable;
import android.util.Log;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @hide
*/
public class SyncRunner extends GraphRunner {
private Scheduler mScheduler = null;
private OnRunnerDoneListener mDoneListener = null;
private ScheduledThreadPoolExecutor mWakeExecutor = new ScheduledThreadPoolExecutor(1);
private ConditionVariable mWakeCondition = new ConditionVariable();
private StopWatchMap mTimer = null;
private final boolean mLogVerbose;
private final static String TAG = "SyncRunner";
// TODO: Provide factory based constructor?
public SyncRunner(FilterContext context, FilterGraph graph, Class schedulerClass) {
super(context);
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
if (mLogVerbose) Log.v(TAG, "Initializing SyncRunner");
// Create the scheduler
if (Scheduler.class.isAssignableFrom(schedulerClass)) {
try {
Constructor schedulerConstructor = schedulerClass.getConstructor(FilterGraph.class);
mScheduler = (Scheduler)schedulerConstructor.newInstance(graph);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Scheduler does not have constructor <init>(FilterGraph)!", e);
} catch (InstantiationException e) {
throw new RuntimeException("Could not instantiate the Scheduler instance!", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access Scheduler constructor!", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Scheduler constructor threw an exception", e);
} catch (Exception e) {
throw new RuntimeException("Could not instantiate Scheduler", e);
}
} else {
throw new IllegalArgumentException("Class provided is not a Scheduler subclass!");
}
// Associate this runner and the graph with the context
mFilterContext = context;
mFilterContext.addGraph(graph);
mTimer = new StopWatchMap();
if (mLogVerbose) Log.v(TAG, "Setting up filters");
// Setup graph filters
graph.setupFilters();
}
@Override
public FilterGraph getGraph() {
return mScheduler != null ? mScheduler.getGraph() : null;
}
public int step() {
assertReadyToStep();
if (!getGraph().isReady() ) {
throw new RuntimeException("Trying to process graph that is not open!");
}
return performStep() ? RESULT_RUNNING : determinePostRunState();
}
public void beginProcessing() {
mScheduler.reset();
getGraph().beginProcessing();
}
public void close() {
// Close filters
if (mLogVerbose) Log.v(TAG, "Closing graph.");
getGraph().closeFilters(mFilterContext);
mScheduler.reset();
}
@Override
public void run() {
if (mLogVerbose) Log.v(TAG, "Beginning run.");
assertReadyToStep();
// Preparation
beginProcessing();
boolean glActivated = activateGlContext();
// Run
boolean keepRunning = true;
while (keepRunning) {
keepRunning = performStep();
}
// Cleanup
if (glActivated) {
deactivateGlContext();
}
// Call completion callback if set
if (mDoneListener != null) {
if (mLogVerbose) Log.v(TAG, "Calling completion listener.");
mDoneListener.onRunnerDone(determinePostRunState());
}
if (mLogVerbose) Log.v(TAG, "Run complete");
}
@Override
public boolean isRunning() {
return false;
}
@Override
public void setDoneCallback(OnRunnerDoneListener listener) {
mDoneListener = listener;
}
@Override
public void stop() {
throw new RuntimeException("SyncRunner does not support stopping a graph!");
}
@Override
synchronized public Exception getError() {
return null;
}
protected void waitUntilWake() {
mWakeCondition.block();
}
protected void processFilterNode(Filter filter) {
if (mLogVerbose) Log.v(TAG, "Processing filter node");
filter.performProcess(mFilterContext);
if (filter.getStatus() == Filter.STATUS_ERROR) {
throw new RuntimeException("There was an error executing " + filter + "!");
} else if (filter.getStatus() == Filter.STATUS_SLEEPING) {
if (mLogVerbose) Log.v(TAG, "Scheduling filter wakeup");
scheduleFilterWake(filter, filter.getSleepDelay());
}
}
protected void scheduleFilterWake(Filter filter, int delay) {
// Close the wake condition
mWakeCondition.close();
// Schedule the wake-up
final Filter filterToSchedule = filter;
final ConditionVariable conditionToWake = mWakeCondition;
mWakeExecutor.schedule(new Runnable() {
@Override
public void run() {
filterToSchedule.unsetStatus(Filter.STATUS_SLEEPING);
conditionToWake.open();
}
}, delay, TimeUnit.MILLISECONDS);
}
protected int determinePostRunState() {
boolean isBlocked = false;
for (Filter filter : mScheduler.getGraph().getFilters()) {
if (filter.isOpen()) {
if (filter.getStatus() == Filter.STATUS_SLEEPING) {
// If ANY node is sleeping, we return our state as sleeping
return RESULT_SLEEPING;
} else {
// If a node is still open, it is blocked (by input or output)
return RESULT_BLOCKED;
}
}
}
return RESULT_FINISHED;
}
// Core internal methods ///////////////////////////////////////////////////////////////////////
boolean performStep() {
if (mLogVerbose) Log.v(TAG, "Performing one step.");
Filter filter = mScheduler.scheduleNextNode();
if (filter != null) {
mTimer.start(filter.getName());
processFilterNode(filter);
mTimer.stop(filter.getName());
return true;
} else {
return false;
}
}
void assertReadyToStep() {
if (mScheduler == null) {
throw new RuntimeException("Attempting to run schedule with no scheduler in place!");
} else if (getGraph() == null) {
throw new RuntimeException("Calling step on scheduler with no graph in place!");
}
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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 android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.FrameManager;
import android.graphics.Bitmap;
import java.nio.ByteBuffer;
/**
* @hide
*/
public class VertexFrame extends Frame {
private int vertexFrameId = -1;
VertexFrame(FrameFormat format, FrameManager frameManager) {
super(format, frameManager);
if (getFormat().getSize() <= 0) {
throw new IllegalArgumentException("Initializing vertex frame with zero size!");
} else {
if (!nativeAllocate(getFormat().getSize())) {
throw new RuntimeException("Could not allocate vertex frame!");
}
}
}
@Override
protected synchronized boolean hasNativeAllocation() {
return vertexFrameId != -1;
}
@Override
protected synchronized void releaseNativeAllocation() {
nativeDeallocate();
vertexFrameId = -1;
}
@Override
public Object getObjectValue() {
throw new RuntimeException("Vertex frames do not support reading data!");
}
@Override
public void setInts(int[] ints) {
assertFrameMutable();
if (!setNativeInts(ints)) {
throw new RuntimeException("Could not set int values for vertex frame!");
}
}
@Override
public int[] getInts() {
throw new RuntimeException("Vertex frames do not support reading data!");
}
@Override
public void setFloats(float[] floats) {
assertFrameMutable();
if (!setNativeFloats(floats)) {
throw new RuntimeException("Could not set int values for vertex frame!");
}
}
@Override
public float[] getFloats() {
throw new RuntimeException("Vertex frames do not support reading data!");
}
@Override
public void setData(ByteBuffer buffer, int offset, int length) {
assertFrameMutable();
byte[] bytes = buffer.array();
if (getFormat().getSize() != bytes.length) {
throw new RuntimeException("Data size in setData does not match vertex frame size!");
} else if (!setNativeData(bytes, offset, length)) {
throw new RuntimeException("Could not set vertex frame data!");
}
}
@Override
public ByteBuffer getData() {
throw new RuntimeException("Vertex frames do not support reading data!");
}
@Override
public void setBitmap(Bitmap bitmap) {
throw new RuntimeException("Unsupported: Cannot set vertex frame bitmap value!");
}
@Override
public Bitmap getBitmap() {
throw new RuntimeException("Vertex frames do not support reading data!");
}
@Override
public void setDataFromFrame(Frame frame) {
// TODO: Optimize
super.setDataFromFrame(frame);
}
public int getVboId() {
return getNativeVboId();
}
@Override
public String toString() {
return "VertexFrame (" + getFormat() + ") with VBO ID " + getVboId();
}
static {
System.loadLibrary("filterfw");
}
private native boolean nativeAllocate(int size);
private native boolean nativeDeallocate();
private native boolean setNativeData(byte[] data, int offset, int length);
private native boolean setNativeInts(int[] ints);
private native boolean setNativeFloats(float[] floats);
private native int getNativeVboId();
}

View File

@@ -0,0 +1,4 @@
/**
* @hide
*/
package android.filterfw.core;

View File

@@ -0,0 +1,92 @@
/*
* 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.format;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.MutableFrameFormat;
import android.graphics.Bitmap;
/**
* @hide
*/
public class ImageFormat {
public static final String COLORSPACE_KEY = "colorspace";
public static final int COLORSPACE_GRAY = 1;
public static final int COLORSPACE_RGB = 2;
public static final int COLORSPACE_RGBA = 3;
public static final int COLORSPACE_YUV = 4;
public static MutableFrameFormat create(int width,
int height,
int colorspace,
int bytesPerSample,
int target) {
MutableFrameFormat result = new MutableFrameFormat(FrameFormat.TYPE_BYTE, target);
result.setDimensions(width, height);
result.setBytesPerSample(bytesPerSample);
result.setMetaValue(COLORSPACE_KEY, colorspace);
if (target == FrameFormat.TARGET_SIMPLE) {
result.setObjectClass(Bitmap.class);
}
return result;
}
public static MutableFrameFormat create(int width,
int height,
int colorspace,
int target) {
return create(width,
height,
colorspace,
bytesPerSampleForColorspace(colorspace),
target);
}
public static MutableFrameFormat create(int colorspace, int target) {
return create(FrameFormat.SIZE_UNSPECIFIED,
FrameFormat.SIZE_UNSPECIFIED,
colorspace,
bytesPerSampleForColorspace(colorspace),
target);
}
public static MutableFrameFormat create(int colorspace) {
return create(FrameFormat.SIZE_UNSPECIFIED,
FrameFormat.SIZE_UNSPECIFIED,
colorspace,
bytesPerSampleForColorspace(colorspace),
FrameFormat.TARGET_UNSPECIFIED);
}
public static int bytesPerSampleForColorspace(int colorspace) {
switch (colorspace) {
case COLORSPACE_GRAY:
return 1;
case COLORSPACE_RGB:
return 3;
case COLORSPACE_RGBA:
return 4;
case COLORSPACE_YUV:
return 3;
default:
throw new RuntimeException("Unknown colorspace id " + colorspace + "!");
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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.format;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.MutableFrameFormat;
import android.filterfw.core.NativeBuffer;
/**
* @hide
*/
public class ObjectFormat {
public static MutableFrameFormat fromClass(Class clazz, int count, int target) {
// Create frame format
MutableFrameFormat result = new MutableFrameFormat(FrameFormat.TYPE_OBJECT, target);
result.setObjectClass(getBoxedClass(clazz));
if (count != FrameFormat.SIZE_UNSPECIFIED) {
result.setDimensions(count);
}
result.setBytesPerSample(bytesPerSampleForClass(clazz, target));
return result;
}
public static MutableFrameFormat fromClass(Class clazz, int target) {
return fromClass(clazz, FrameFormat.SIZE_UNSPECIFIED, target);
}
public static MutableFrameFormat fromObject(Object object, int target) {
return object == null
? new MutableFrameFormat(FrameFormat.TYPE_OBJECT, target)
: fromClass(object.getClass(), FrameFormat.SIZE_UNSPECIFIED, target);
}
public static MutableFrameFormat fromObject(Object object, int count, int target) {
return object == null
? new MutableFrameFormat(FrameFormat.TYPE_OBJECT, target)
: fromClass(object.getClass(), count, target);
}
private static int bytesPerSampleForClass(Class clazz, int target) {
// Native targets have objects manifested in a byte buffer. Thus it is important to
// correctly determine the size of single element here.
if (target == FrameFormat.TARGET_NATIVE) {
if (!NativeBuffer.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException("Native object-based formats must be of a " +
"NativeBuffer subclass! (Received class: " + clazz + ").");
}
try {
return ((NativeBuffer)clazz.newInstance()).getElementSize();
} catch (Exception e) {
throw new RuntimeException("Could not determine the size of an element in a "
+ "native object-based frame of type " + clazz + "! Perhaps it is missing a "
+ "default constructor?");
}
} else {
return FrameFormat.BYTES_PER_SAMPLE_UNSPECIFIED;
}
}
private static Class getBoxedClass(Class type) {
// Check if type is primitive
if (type.isPrimitive()) {
// Yes -> box it
if (type == boolean.class) {
return java.lang.Boolean.class;
} else if (type == byte.class) {
return java.lang.Byte.class;
} else if (type == char.class) {
return java.lang.Character.class;
} else if (type == short.class) {
return java.lang.Short.class;
} else if (type == int.class) {
return java.lang.Integer.class;
} else if (type == long.class) {
return java.lang.Long.class;
} else if (type == float.class) {
return java.lang.Float.class;
} else if (type == double.class) {
return java.lang.Double.class;
} else {
throw new IllegalArgumentException(
"Unknown primitive type: " + type.getSimpleName() + "!");
}
} else {
// No -> return it
return type;
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.format;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.MutableFrameFormat;
/**
* @hide
*/
public class PrimitiveFormat {
public static MutableFrameFormat createByteFormat(int count, int target) {
return createFormat(FrameFormat.TYPE_BYTE, count, target);
}
public static MutableFrameFormat createInt16Format(int count, int target) {
return createFormat(FrameFormat.TYPE_INT16, count, target);
}
public static MutableFrameFormat createInt32Format(int count, int target) {
return createFormat(FrameFormat.TYPE_INT32, count, target);
}
public static MutableFrameFormat createFloatFormat(int count, int target) {
return createFormat(FrameFormat.TYPE_FLOAT, count, target);
}
public static MutableFrameFormat createDoubleFormat(int count, int target) {
return createFormat(FrameFormat.TYPE_DOUBLE, count, target);
}
public static MutableFrameFormat createByteFormat(int target) {
return createFormat(FrameFormat.TYPE_BYTE, target);
}
public static MutableFrameFormat createInt16Format(int target) {
return createFormat(FrameFormat.TYPE_INT16, target);
}
public static MutableFrameFormat createInt32Format(int target) {
return createFormat(FrameFormat.TYPE_INT32, target);
}
public static MutableFrameFormat createFloatFormat(int target) {
return createFormat(FrameFormat.TYPE_FLOAT, target);
}
public static MutableFrameFormat createDoubleFormat(int target) {
return createFormat(FrameFormat.TYPE_DOUBLE, target);
}
private static MutableFrameFormat createFormat(int baseType, int count, int target) {
MutableFrameFormat result = new MutableFrameFormat(baseType, target);
result.setDimensions(count);
return result;
}
private static MutableFrameFormat createFormat(int baseType, int target) {
MutableFrameFormat result = new MutableFrameFormat(baseType, target);
result.setDimensionCount(1);
return result;
}
}

View File

@@ -0,0 +1,4 @@
/**
* @hide
*/
package android.filterfw.format;

View File

@@ -0,0 +1,113 @@
/*
* 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.geometry;
import java.lang.Math;
/**
* @hide
*/
public class Point {
public float x;
public float y;
public Point() {
}
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public void set(float x, float y) {
this.x = x;
this.y = y;
}
public boolean IsInUnitRange() {
return x >= 0.0f && x <= 1.0f &&
y >= 0.0f && y <= 1.0f;
}
public Point plus(float x, float y) {
return new Point(this.x + x, this.y + y);
}
public Point plus(Point point) {
return this.plus(point.x, point.y);
}
public Point minus(float x, float y) {
return new Point(this.x - x, this.y - y);
}
public Point minus(Point point) {
return this.minus(point.x, point.y);
}
public Point times(float s) {
return new Point(this.x * s, this.y * s);
}
public Point mult(float x, float y) {
return new Point(this.x * x, this.y * y);
}
public float length() {
return (float)Math.sqrt(x*x + y*y);
}
public float distanceTo(Point p) {
return p.minus(this).length();
}
public Point scaledTo(float length) {
return this.times(length / this.length());
}
public Point normalize() {
return this.scaledTo(1.0f);
}
public Point rotated90(int count) {
float nx = this.x;
float ny = this.y;
for (int i = 0; i < count; ++i) {
float ox = nx;
nx = ny;
ny = -ox;
}
return new Point(nx, ny);
}
public Point rotated(float radians) {
// TODO(renn): Optimize: Keep cache of cos/sin values
return new Point((float)(Math.cos(radians) * x - Math.sin(radians) * y),
(float)(Math.sin(radians) * x + Math.cos(radians) * y));
}
public Point rotatedAround(Point center, float radians) {
return this.minus(center).rotated(radians).plus(center);
}
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.geometry;
import android.filterfw.geometry.Point;
import java.lang.Float;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @hide
*/
public class Quad {
public Point p0;
public Point p1;
public Point p2;
public Point p3;
public Quad() {
}
public Quad(Point p0, Point p1, Point p2, Point p3) {
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
public boolean IsInUnitRange() {
return p0.IsInUnitRange() &&
p1.IsInUnitRange() &&
p2.IsInUnitRange() &&
p3.IsInUnitRange();
}
public Quad translated(Point t) {
return new Quad(p0.plus(t), p1.plus(t), p2.plus(t), p3.plus(t));
}
public Quad translated(float x, float y) {
return new Quad(p0.plus(x, y), p1.plus(x, y), p2.plus(x, y), p3.plus(x, y));
}
public Quad scaled(float s) {
return new Quad(p0.times(s), p1.times(s), p2.times(s), p3.times(s));
}
public Quad scaled(float x, float y) {
return new Quad(p0.mult(x, y), p1.mult(x, y), p2.mult(x, y), p3.mult(x, y));
}
public Rectangle boundingBox() {
List<Float> xs = Arrays.asList(p0.x, p1.x, p2.x, p3.x);
List<Float> ys = Arrays.asList(p0.y, p1.y, p2.y, p3.y);
float x0 = Collections.min(xs);
float y0 = Collections.min(ys);
float x1 = Collections.max(xs);
float y1 = Collections.max(ys);
return new Rectangle(x0, y0, x1 - x0, y1 - y0);
}
public float getBoundingWidth() {
List<Float> xs = Arrays.asList(p0.x, p1.x, p2.x, p3.x);
return Collections.max(xs) - Collections.min(xs);
}
public float getBoundingHeight() {
List<Float> ys = Arrays.asList(p0.y, p1.y, p2.y, p3.y);
return Collections.max(ys) - Collections.min(ys);
}
@Override
public String toString() {
return "{" + p0 + ", " + p1 + ", " + p2 + ", " + p3 + "}";
}
}

View File

@@ -0,0 +1,95 @@
/*
* 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.geometry;
import android.filterfw.geometry.Point;
import android.filterfw.geometry.Quad;
/**
* @hide
*/
public class Rectangle extends Quad {
public Rectangle() {
}
public Rectangle(float x, float y, float width, float height) {
super(new Point(x, y),
new Point(x + width, y),
new Point(x, y + height),
new Point(x + width, y + height));
}
public Rectangle(Point origin, Point size) {
super(origin,
origin.plus(size.x, 0.0f),
origin.plus(0.0f, size.y),
origin.plus(size.x, size.y));
}
public static Rectangle fromRotatedRect(Point center, Point size, float rotation) {
Point p0 = new Point(center.x - size.x/2f, center.y - size.y/2f);
Point p1 = new Point(center.x + size.x/2f, center.y - size.y/2f);
Point p2 = new Point(center.x - size.x/2f, center.y + size.y/2f);
Point p3 = new Point(center.x + size.x/2f, center.y + size.y/2f);
return new Rectangle(p0.rotatedAround(center, rotation),
p1.rotatedAround(center, rotation),
p2.rotatedAround(center, rotation),
p3.rotatedAround(center, rotation));
}
private Rectangle(Point p0, Point p1, Point p2, Point p3) {
super(p0, p1, p2, p3);
}
public static Rectangle fromCenterVerticalAxis(Point center, Point vAxis, Point size) {
Point dy = vAxis.scaledTo(size.y / 2.0f);
Point dx = vAxis.rotated90(1).scaledTo(size.x / 2.0f);
return new Rectangle(center.minus(dx).minus(dy),
center.plus(dx).minus(dy),
center.minus(dx).plus(dy),
center.plus(dx).plus(dy));
}
public float getWidth() {
return p1.minus(p0).length();
}
public float getHeight() {
return p2.minus(p0).length();
}
public Point center() {
return p0.plus(p1).plus(p2).plus(p3).times(0.25f);
}
@Override
public Rectangle scaled(float s) {
return new Rectangle(p0.times(s), p1.times(s), p2.times(s), p3.times(s));
}
@Override
public Rectangle scaled(float x, float y) {
return new Rectangle(p0.mult(x, y), p1.mult(x, y), p2.mult(x, y), p3.mult(x, y));
}
//public Rectangle rotated(float radians) {
// TODO: Implement this.
//}
}

View File

@@ -0,0 +1,4 @@
/**
* @hide
*/
package android.filterfw.geometry;

View File

@@ -0,0 +1,33 @@
/*
* 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.io;
/**
* @hide
*/
public class GraphIOException extends Exception {
public GraphIOException() {
super();
}
public GraphIOException(String message) {
super(message);
}
}

Some files were not shown because too many files have changed in this diff Show More