summaryrefslogtreecommitdiffstats
path: root/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
diff options
context:
space:
mode:
Diffstat (limited to 'media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java')
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java265
1 files changed, 265 insertions, 0 deletions
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
new file mode 100644
index 0000000..37fa242
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
@@ -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.filterpacks.videosrc;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer;
+import android.os.ConditionVariable;
+import android.opengl.Matrix;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.lang.IllegalArgumentException;
+import java.util.List;
+import java.util.Set;
+
+import android.util.Log;
+
+/** <p>A filter that converts textures from a SurfaceTexture object into frames for
+ * processing in the filter framework.</p>
+ *
+ * <p>To use, connect up the sourceListener callback, and then when executing
+ * the graph, use the SurfaceTexture object passed to the callback to feed
+ * frames into the filter graph. For example, pass the SurfaceTexture into
+ * {#link
+ * android.hardware.Camera.setPreviewTexture(android.graphics.SurfaceTexture)}.
+ * This filter is intended for applications that need for flexibility than the
+ * CameraSource and MediaSource provide. Note that the application needs to
+ * provide width and height information for the SurfaceTextureSource, which it
+ * should obtain from wherever the SurfaceTexture data is coming from to avoid
+ * unnecessary resampling.</p>
+ *
+ * @hide
+ */
+public class SurfaceTextureSource extends Filter {
+
+ /** User-visible parameters */
+
+ /** The callback interface for the sourceListener parameter */
+ public interface SurfaceTextureSourceListener {
+ public void onSurfaceTextureSourceReady(SurfaceTexture source);
+ }
+ /** A callback to send the internal SurfaceTexture object to, once it is
+ * created. This callback will be called when the the filter graph is
+ * preparing to execute, but before any processing has actually taken
+ * place. The SurfaceTexture object passed to this callback is the only way
+ * to feed this filter. When the filter graph is shutting down, this
+ * callback will be called again with null as the source.
+ *
+ * This callback may be called from an arbitrary thread, so it should not
+ * assume it is running in the UI thread in particular.
+ */
+ @GenerateFinalPort(name = "sourceListener")
+ private SurfaceTextureSourceListener mSourceListener;
+
+ /** The width of the output image frame. If the texture width for the
+ * SurfaceTexture source is known, use it here to minimize resampling. */
+ @GenerateFieldPort(name = "width")
+ private int mWidth;
+
+ /** The height of the output image frame. If the texture height for the
+ * SurfaceTexture source is known, use it here to minimize resampling. */
+ @GenerateFieldPort(name = "height")
+ private int mHeight;
+
+ /** Whether the filter will always wait for a new frame from its
+ * SurfaceTexture, or whether it will output an old frame again if a new
+ * frame isn't available. The filter will always wait for the first frame,
+ * to avoid outputting a blank frame. Defaults to true.
+ */
+ @GenerateFieldPort(name = "waitForNewFrame", hasDefault = true)
+ private boolean mWaitForNewFrame = true;
+
+ /** Maximum timeout before signaling error when waiting for a new frame. Set
+ * this to zero to disable the timeout and wait indefinitely. In milliseconds.
+ */
+ @GenerateFieldPort(name = "waitTimeout", hasDefault = true)
+ private int mWaitTimeout = 1000;
+
+ /** Whether a timeout is an exception-causing failure, or just causes the
+ * filter to close.
+ */
+ @GenerateFieldPort(name = "closeOnTimeout", hasDefault = true)
+ private boolean mCloseOnTimeout = false;
+
+ // Variables for input->output conversion
+ private GLFrame mMediaFrame;
+ private ShaderProgram mFrameExtractor;
+ private SurfaceTexture mSurfaceTexture;
+ private MutableFrameFormat mOutputFormat;
+ private ConditionVariable mNewFrameAvailable;
+ private boolean mFirstFrame;
+
+ private float[] mFrameTransform;
+ private float[] mMappedCoords;
+ // These default source coordinates perform the necessary flip
+ // for converting from MFF/Bitmap origin to OpenGL origin.
+ private static final float[] mSourceCoords = { 0, 1, 0, 1,
+ 1, 1, 0, 1,
+ 0, 0, 0, 1,
+ 1, 0, 0, 1 };
+ // Shader for output
+ private final String mRenderShader =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "uniform samplerExternalOES tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+ "}\n";
+
+ // Variables for logging
+
+ private static final String TAG = "SurfaceTextureSource";
+ private static final boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+ public SurfaceTextureSource(String name) {
+ super(name);
+ mNewFrameAvailable = new ConditionVariable();
+ mFrameTransform = new float[16];
+ mMappedCoords = new float[16];
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input port
+ addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ private void createFormats() {
+ mOutputFormat = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ }
+
+ @Override
+ protected void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Preparing SurfaceTextureSource");
+
+ createFormats();
+
+ // Prepare input
+ mMediaFrame = (GLFrame)context.getFrameManager().newBoundFrame(mOutputFormat,
+ GLFrame.EXTERNAL_TEXTURE,
+ 0);
+
+ // Prepare output
+ mFrameExtractor = new ShaderProgram(context, mRenderShader);
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Opening SurfaceTextureSource");
+ // Create SurfaceTexture anew each time - it can use substantial memory.
+ mSurfaceTexture = new SurfaceTexture(mMediaFrame.getTextureId());
+ // Connect SurfaceTexture to callback
+ mSurfaceTexture.setOnFrameAvailableListener(onFrameAvailableListener);
+ // Connect SurfaceTexture to source
+ mSourceListener.onSurfaceTextureSourceReady(mSurfaceTexture);
+ mFirstFrame = true;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Processing new frame");
+
+ // First, get new frame if available
+ if (mWaitForNewFrame || mFirstFrame) {
+ boolean gotNewFrame;
+ if (mWaitTimeout != 0) {
+ gotNewFrame = mNewFrameAvailable.block(mWaitTimeout);
+ if (!gotNewFrame) {
+ if (!mCloseOnTimeout) {
+ throw new RuntimeException("Timeout waiting for new frame");
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Timeout waiting for a new frame. Closing.");
+ closeOutputPort("video");
+ return;
+ }
+ }
+ } else {
+ mNewFrameAvailable.block();
+ }
+ mNewFrameAvailable.close();
+ mFirstFrame = false;
+ }
+
+ mSurfaceTexture.updateTexImage();
+
+ mSurfaceTexture.getTransformMatrix(mFrameTransform);
+ Matrix.multiplyMM(mMappedCoords, 0,
+ mFrameTransform, 0,
+ mSourceCoords, 0);
+ mFrameExtractor.setSourceRegion(mMappedCoords[0], mMappedCoords[1],
+ mMappedCoords[4], mMappedCoords[5],
+ mMappedCoords[8], mMappedCoords[9],
+ mMappedCoords[12], mMappedCoords[13]);
+ // Next, render to output
+ Frame output = context.getFrameManager().newFrame(mOutputFormat);
+ mFrameExtractor.process(mMediaFrame, output);
+
+ output.setTimestamp(mSurfaceTexture.getTimestamp());
+
+ pushOutput("video", output);
+ output.release();
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "SurfaceTextureSource closed");
+ mSourceListener.onSurfaceTextureSourceReady(null);
+ mSurfaceTexture.release();
+ mSurfaceTexture = null;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mMediaFrame != null) {
+ mMediaFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (name.equals("width") || name.equals("height") ) {
+ mOutputFormat.setDimensions(mWidth, mHeight);
+ }
+ }
+
+ private SurfaceTexture.OnFrameAvailableListener onFrameAvailableListener =
+ new SurfaceTexture.OnFrameAvailableListener() {
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ if (mLogVerbose) Log.v(TAG, "New frame from SurfaceTexture");
+ mNewFrameAvailable.open();
+ }
+ };
+}