165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn/*
265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Copyright (C) 2011 The Android Open Source Project
365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Licensed under the Apache License, Version 2.0 (the "License");
565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * you may not use this file except in compliance with the License.
665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * You may obtain a copy of the License at
765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *      http://www.apache.org/licenses/LICENSE-2.0
965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
1065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Unless required by applicable law or agreed to in writing, software
1165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * distributed under the License is distributed on an "AS IS" BASIS,
1265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * See the License for the specific language governing permissions and
1465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * limitations under the License.
1565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn */
1665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
1765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
1865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennpackage android.filterpacks.videosrc;
1965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
2065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.content.Context;
2165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.Filter;
2265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.FilterContext;
2365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.Frame;
2465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.FrameFormat;
2565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.FrameManager;
2665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.GenerateFieldPort;
2765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.GenerateFinalPort;
2865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.GLFrame;
2965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.KeyValueMap;
3065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.MutableFrameFormat;
3165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.NativeFrame;
3265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.Program;
3365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.ShaderProgram;
3465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.format.ImageFormat;
3565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.graphics.SurfaceTexture;
3665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.hardware.Camera;
3765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.os.ConditionVariable;
3865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.opengl.Matrix;
3965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.io.IOException;
4165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.List;
4265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.Set;
4365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.util.Log;
4565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn/**
4765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * @hide
4865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn */
4965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennpublic class CameraSource extends Filter {
5065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** User-visible parameters */
5265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Camera ID to use for input. Defaults to 0. */
5465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "id", hasDefault = true)
5565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int mCameraId = 0;
5665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Frame width to request from camera. Actual size may not match requested. */
5865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "width", hasDefault = true)
5965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int mWidth = 320;
6065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
6165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Frame height to request from camera. Actual size may not match requested. */
6265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "height", hasDefault = true)
6365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int mHeight = 240;
6465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
6565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Stream framerate to request from camera. Actual frame rate may not match requested. */
6665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFieldPort(name = "framerate", hasDefault = true)
6765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int mFps = 30;
6865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
6965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Whether the filter should always wait for a new frame from the camera
7065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * before providing output.  If set to false, the filter will keep
7165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * outputting the last frame it received from the camera if multiple process
7265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * calls are received before the next update from the Camera. Defaults to true.
7365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     */
7465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @GenerateFinalPort(name = "waitForNewFrame", hasDefault = true)
7565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mWaitForNewFrame = true;
7665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
7765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private Camera mCamera;
7865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private GLFrame mCameraFrame;
7965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private SurfaceTexture mSurfaceTexture;
8065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private ShaderProgram mFrameExtractor;
8165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private MutableFrameFormat mOutputFormat;
8265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
8365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private float[] mCameraTransform;
8465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private float[] mMappedCoords;
8565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // These default source coordinates perform the necessary flip
8665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    // for converting from OpenGL origin to MFF/Bitmap origin.
8765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final float[] mSourceCoords = { 0, 1, 0, 1,
8865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                   1, 1, 0, 1,
8965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                   0, 0, 0, 1,
9065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                   1, 0, 0, 1 };
9165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
9265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final int NEWFRAME_TIMEOUT = 100; //ms
9365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final int NEWFRAME_TIMEOUT_REPEAT = 10;
9465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
9565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private boolean mNewFrameAvailable;
9665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
9765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private Camera.Parameters mCameraParameters;
9865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
9965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final String mFrameShader =
10065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "#extension GL_OES_EGL_image_external : require\n" +
10165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "precision mediump float;\n" +
10265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "uniform samplerExternalOES tex_sampler_0;\n" +
10365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "varying vec2 v_texcoord;\n" +
10465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "void main() {\n" +
10565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
10665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            "}\n";
10765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
10865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private final boolean mLogVerbose;
10965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private static final String TAG = "CameraSource";
11065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
11165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public CameraSource(String name) {
11265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        super(name);
11365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCameraTransform = new float[16];
11465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mMappedCoords = new float[16];
11565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
11665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
11765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
11865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
11965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
12065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void setupPorts() {
12165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Add input port
12265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
12365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                  FrameFormat.TARGET_GPU));
12465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
12565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
12665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void createFormats() {
12765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mOutputFormat = ImageFormat.create(mWidth, mHeight,
12865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                           ImageFormat.COLORSPACE_RGBA,
12965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                           FrameFormat.TARGET_GPU);
13065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
13165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
13265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
13365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void prepare(FilterContext context) {
13465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Preparing");
13565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Compile shader TODO: Move to onGLEnvSomething?
13665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mFrameExtractor = new ShaderProgram(context, mFrameShader);
13765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
13865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
13965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
14065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void open(FilterContext context) {
14165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Opening");
14265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Open camera
14365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCamera = Camera.open(mCameraId);
14465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
14565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Set parameters
14665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        getCameraParameters();
14765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCamera.setParameters(mCameraParameters);
14865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
14965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Create frame formats
15065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        createFormats();
15165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
15265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Bind it to our camera frame
15365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCameraFrame = (GLFrame)context.getFrameManager().newBoundFrame(mOutputFormat,
15465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                                        GLFrame.EXTERNAL_TEXTURE,
15565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                                        0);
15665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture = new SurfaceTexture(mCameraFrame.getTextureId());
15765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        try {
15865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mCamera.setPreviewTexture(mSurfaceTexture);
15965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } catch (IOException e) {
16065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new RuntimeException("Could not bind camera surface texture: " +
16165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                       e.getMessage() + "!");
16265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
16365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
16465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Connect SurfaceTexture to callback
16565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture.setOnFrameAvailableListener(onCameraFrameAvailableListener);
16665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Start the preview
16765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mNewFrameAvailable = false;
16865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCamera.startPreview();
16965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
17065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
17165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
17265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void process(FilterContext context) {
17365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Processing new frame");
17465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
17565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mWaitForNewFrame) {
17665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            int waitCount = 0;
17765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            while (!mNewFrameAvailable) {
17865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (waitCount == NEWFRAME_TIMEOUT_REPEAT) {
17965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    throw new RuntimeException("Timeout waiting for new frame");
18065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
18165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                try {
18265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    this.wait(NEWFRAME_TIMEOUT);
18365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                } catch (InterruptedException e) {
18465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    if (mLogVerbose) Log.v(TAG, "Interrupted while waiting for new frame");
18565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
18665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
18765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mNewFrameAvailable = false;
18865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "Got new frame");
18965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
19065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
19165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture.updateTexImage();
19265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
19365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Using frame extractor in thread: " + Thread.currentThread());
19465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture.getTransformMatrix(mCameraTransform);
19565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        Matrix.multiplyMM(mMappedCoords, 0,
19665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                          mCameraTransform, 0,
19765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                          mSourceCoords, 0);
19865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mFrameExtractor.setSourceRegion(mMappedCoords[0], mMappedCoords[1],
19965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                        mMappedCoords[4], mMappedCoords[5],
20065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                        mMappedCoords[8], mMappedCoords[9],
20165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                        mMappedCoords[12], mMappedCoords[13]);
20265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
20365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        Frame output = context.getFrameManager().newFrame(mOutputFormat);
20465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mFrameExtractor.process(mCameraFrame, output);
20565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
20665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        long timestamp = mSurfaceTexture.getTimestamp();
20765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Timestamp: " + (timestamp / 1000000000.0) + " s");
20865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        output.setTimestamp(timestamp);
20965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
21065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        pushOutput("video", output);
21165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
21265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Release pushed frame
21365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        output.release();
21465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
21565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Done processing new frame");
21665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
21765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
21865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
21965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void close(FilterContext context) {
22065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Closing");
22165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
22265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCamera.release();
22365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCamera = null;
22465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture.release();
22565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSurfaceTexture = null;
22665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
22765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
22865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
22965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void tearDown(FilterContext context) {
23065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mCameraFrame != null) {
23165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mCameraFrame.release();
23265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
23365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
23465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
23565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
23665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public void fieldPortValueUpdated(String name, FilterContext context) {
23765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (name.equals("framerate")) {
23865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            getCameraParameters();
23965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            int closestRange[] = findClosestFpsRange(mFps, mCameraParameters);
24065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mCameraParameters.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
24165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                 closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
24265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mCamera.setParameters(mCameraParameters);
24365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
24465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
24565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
24665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    synchronized public Camera.Parameters getCameraParameters() {
24765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        boolean closeCamera = false;
24865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mCameraParameters == null) {
24965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mCamera == null) {
25065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mCamera = Camera.open(mCameraId);
25165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                closeCamera = true;
25265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
25365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mCameraParameters = mCamera.getParameters();
25465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
25565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (closeCamera) {
25665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mCamera.release();
25765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mCamera = null;
25865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
25965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
26065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
26165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int closestSize[] = findClosestSize(mWidth, mHeight, mCameraParameters);
26265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mWidth = closestSize[0];
26365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mHeight = closestSize[1];
26465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCameraParameters.setPreviewSize(mWidth, mHeight);
26565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
26665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int closestRange[] = findClosestFpsRange(mFps, mCameraParameters);
26765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
26865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCameraParameters.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
26965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                             closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
27065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
27165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return mCameraParameters;
27265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
27365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
27465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /** Update camera parameters. Image resolution cannot be changed. */
27565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    synchronized public void setCameraParameters(Camera.Parameters params) {
27665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        params.setPreviewSize(mWidth, mHeight);
27765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCameraParameters = params;
27865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (isOpen()) {
27965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mCamera.setParameters(mCameraParameters);
28065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
28165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
28265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
28365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int[] findClosestSize(int width, int height, Camera.Parameters parameters) {
28465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
28565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int closestWidth = -1;
28665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int closestHeight = -1;
28765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int smallestWidth = previewSizes.get(0).width;
28865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int smallestHeight =  previewSizes.get(0).height;
28965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (Camera.Size size : previewSizes) {
29065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Best match defined as not being larger in either dimension than
29165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // the requested size, but as close as possible. The below isn't a
29265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // stable selection (reording the size list can give different
29365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // results), but since this is a fallback nicety, that's acceptable.
29465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if ( size.width <= width &&
29565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                 size.height <= height &&
29665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                 size.width >= closestWidth &&
29765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                 size.height >= closestHeight) {
29865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                closestWidth = size.width;
29965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                closestHeight = size.height;
30065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
30165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if ( size.width < smallestWidth &&
30265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                 size.height < smallestHeight) {
30365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                smallestWidth = size.width;
30465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                smallestHeight = size.height;
30565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
30665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
30765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (closestWidth == -1) {
30865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Requested size is smaller than any listed size; match with smallest possible
30965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            closestWidth = smallestWidth;
31065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            closestHeight = smallestHeight;
31165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
31265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
31365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) {
31465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            Log.v(TAG,
31565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                  "Requested resolution: (" + width + ", " + height
31665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                  + "). Closest match: (" + closestWidth + ", "
31765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                  + closestHeight + ").");
31865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
31965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int[] closestSize = {closestWidth, closestHeight};
32065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return closestSize;
32165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
32265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
32365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private int[] findClosestFpsRange(int fps, Camera.Parameters params) {
32465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
32565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int[] closestRange = supportedFpsRanges.get(0);
32665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (int[] range : supportedFpsRanges) {
32765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] < fps*1000 &&
32865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] > fps*1000 &&
32965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] >
33065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] &&
33165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] <
33265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
33365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                closestRange = range;
33465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
33565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
33665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mLogVerbose) Log.v(TAG, "Requested fps: " + fps
33765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                               + ".Closest frame rate range: ["
33865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                               + closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.
33965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                               + ","
34065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                               + closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.
34165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                               + "]");
34265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
34365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return closestRange;
34465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
34565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
34665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private SurfaceTexture.OnFrameAvailableListener onCameraFrameAvailableListener =
34765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            new SurfaceTexture.OnFrameAvailableListener() {
34865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        @Override
34965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void onFrameAvailable(SurfaceTexture surfaceTexture) {
35065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (mLogVerbose) Log.v(TAG, "New frame from camera");
35165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            synchronized(CameraSource.this) {
35265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mNewFrameAvailable = true;
35365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                CameraSource.this.notify();
35465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
35565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
35665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    };
35765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
35865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn}
359