14a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala/*
24a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala * Copyright (C) 2011 The Android Open Source Project
34a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala *
44a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala * Licensed under the Apache License, Version 2.0 (the "License");
54a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala * you may not use this file except in compliance with the License.
64a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala * You may obtain a copy of the License at
74a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala *
84a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala *      http://www.apache.org/licenses/LICENSE-2.0
94a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala *
104a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala * Unless required by applicable law or agreed to in writing, software
114a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala * distributed under the License is distributed on an "AS IS" BASIS,
124a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala * See the License for the specific language governing permissions and
144a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala * limitations under the License.
154a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala */
164a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
174a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
184a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalapackage android.filterpacks.videosrc;
194a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
204a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.content.Context;
214a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.Filter;
224a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.FilterContext;
234a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.Frame;
244a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.FrameFormat;
254a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.FrameManager;
2621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.core.GenerateFieldPort;
2721d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.core.GenerateFinalPort;
2821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.core.GLFrame;
294a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.KeyValueMap;
304a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.MutableFrameFormat;
314a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.NativeFrame;
324a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.Program;
334a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.filterfw.core.ShaderProgram;
3421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.format.ImageFormat;
354a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.graphics.SurfaceTexture;
364a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.hardware.Camera;
374a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.os.ConditionVariable;
38855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvalaimport android.opengl.Matrix;
394a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
404a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport java.io.IOException;
414a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport java.util.List;
424a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport java.util.Set;
434a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
444a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalaimport android.util.Log;
454a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
46a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala/**
47a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala * @hide
48a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala */
494a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvalapublic class CameraSource extends Filter {
504a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
514a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    /** User-visible parameters */
524a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
534a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    /** Camera ID to use for input. Defaults to 0. */
5421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFieldPort(name = "id", hasDefault = true)
554a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private int mCameraId = 0;
564a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
574a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    /** Frame width to request from camera. Actual size may not match requested. */
5821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFieldPort(name = "width", hasDefault = true)
594a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private int mWidth = 320;
604a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
614a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    /** Frame height to request from camera. Actual size may not match requested. */
6221d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFieldPort(name = "height", hasDefault = true)
634a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private int mHeight = 240;
644a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
654a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    /** Stream framerate to request from camera. Actual frame rate may not match requested. */
6621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFieldPort(name = "framerate", hasDefault = true)
674a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private int mFps = 30;
684a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
694a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    /** Whether the filter should always wait for a new frame from the camera
704a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala     * before providing output.  If set to false, the filter will keep
714a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala     * outputting the last frame it received from the camera if multiple process
724a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala     * calls are received before the next update from the Camera. Defaults to true.
734a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala     */
7421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFinalPort(name = "waitForNewFrame", hasDefault = true)
754a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private boolean mWaitForNewFrame = true;
764a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
774a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private Camera mCamera;
784a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private GLFrame mCameraFrame;
794a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private SurfaceTexture mSurfaceTexture;
804a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private ShaderProgram mFrameExtractor;
814a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private MutableFrameFormat mOutputFormat;
82855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala
834a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private float[] mCameraTransform;
84855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala    private float[] mMappedCoords;
85855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala    // These default source coordinates perform the necessary flip
86855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala    // for converting from OpenGL origin to MFF/Bitmap origin.
87855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala    private static final float[] mSourceCoords = { 0, 1, 0, 1,
88855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala                                                   1, 1, 0, 1,
89855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala                                                   0, 0, 0, 1,
90855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala                                                   1, 0, 0, 1 };
914a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
92e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala    private static final int NEWFRAME_TIMEOUT = 100; //ms
93e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala    private static final int NEWFRAME_TIMEOUT_REPEAT = 10;
94e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala
95e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala    private boolean mNewFrameAvailable;
96e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala
9724455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala    private Camera.Parameters mCameraParameters;
9824455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala
9921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    private static final String mFrameShader =
1004a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            "#extension GL_OES_EGL_image_external : require\n" +
1014a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            "precision mediump float;\n" +
1024a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            "uniform samplerExternalOES tex_sampler_0;\n" +
1034a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            "varying vec2 v_texcoord;\n" +
1044a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            "void main() {\n" +
105855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala            "  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
1064a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            "}\n";
1074a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1088dd704358d808382465666354fc891af59b21e18Eino-Ville Talvala    private final boolean mLogVerbose;
1094a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private static final String TAG = "CameraSource";
1104a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1114a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    public CameraSource(String name) {
1124a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        super(name);
1134a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mCameraTransform = new float[16];
114855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala        mMappedCoords = new float[16];
11557774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala
11657774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
1174a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
1184a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1194a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    @Override
12021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void setupPorts() {
12121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        // Add input port
12221d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
12321d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn                                                  FrameFormat.TARGET_GPU));
1244a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
1254a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
12621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    private void createFormats() {
12721d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        mOutputFormat = ImageFormat.create(mWidth, mHeight,
12821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn                                           ImageFormat.COLORSPACE_RGBA,
12921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn                                           FrameFormat.TARGET_GPU);
1304a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
1314a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1324a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    @Override
1334a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    public void prepare(FilterContext context) {
13457774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        if (mLogVerbose) Log.v(TAG, "Preparing");
1354a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        // Compile shader TODO: Move to onGLEnvSomething?
136511360e61650864ea22a171159efe073c80d0cdbMarius Renn        mFrameExtractor = new ShaderProgram(context, mFrameShader);
1374a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
1384a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1394a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    @Override
14021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void open(FilterContext context) {
14157774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        if (mLogVerbose) Log.v(TAG, "Opening");
1424a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        // Open camera
1434a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mCamera = Camera.open(mCameraId);
1444a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1454a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        // Set parameters
14624455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala        getCameraParameters();
14724455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala        mCamera.setParameters(mCameraParameters);
1484a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
14921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        // Create frame formats
15021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        createFormats();
15121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn
1524a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        // Bind it to our camera frame
153edfc0941e7a9480d2bbc70e842015e6fa56a2c3fEddy Talvala        mCameraFrame = (GLFrame)context.getFrameManager().newBoundFrame(mOutputFormat,
154edfc0941e7a9480d2bbc70e842015e6fa56a2c3fEddy Talvala                                                                        GLFrame.EXTERNAL_TEXTURE,
155edfc0941e7a9480d2bbc70e842015e6fa56a2c3fEddy Talvala                                                                        0);
1564a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mSurfaceTexture = new SurfaceTexture(mCameraFrame.getTextureId());
1574a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        try {
1584a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            mCamera.setPreviewTexture(mSurfaceTexture);
1594a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        } catch (IOException e) {
1604a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            throw new RuntimeException("Could not bind camera surface texture: " +
1614a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala                                       e.getMessage() + "!");
1624a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        }
1634a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1644a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        // Connect SurfaceTexture to callback
1654a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mSurfaceTexture.setOnFrameAvailableListener(onCameraFrameAvailableListener);
1664a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        // Start the preview
167e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala        mNewFrameAvailable = false;
1684a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mCamera.startPreview();
1694a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
1704a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1714a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    @Override
17221d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void process(FilterContext context) {
17357774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        if (mLogVerbose) Log.v(TAG, "Processing new frame");
1744a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1754a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        if (mWaitForNewFrame) {
176e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala            int waitCount = 0;
177e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala            while (!mNewFrameAvailable) {
178e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                if (waitCount == NEWFRAME_TIMEOUT_REPEAT) {
179e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                    throw new RuntimeException("Timeout waiting for new frame");
180e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                }
181e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                try {
182e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                    this.wait(NEWFRAME_TIMEOUT);
183e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                } catch (InterruptedException e) {
184e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                    if (mLogVerbose) Log.v(TAG, "Interrupted while waiting for new frame");
185e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                }
1864a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            }
187e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala            mNewFrameAvailable = false;
188e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala            if (mLogVerbose) Log.v(TAG, "Got new frame");
1894a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        }
1904a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
1914a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mSurfaceTexture.updateTexImage();
1924a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
19357774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        if (mLogVerbose) Log.v(TAG, "Using frame extractor in thread: " + Thread.currentThread());
1944a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mSurfaceTexture.getTransformMatrix(mCameraTransform);
195855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala        Matrix.multiplyMM(mMappedCoords, 0,
196855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala                          mCameraTransform, 0,
197855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala                          mSourceCoords, 0);
198855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala        mFrameExtractor.setSourceRegion(mMappedCoords[0], mMappedCoords[1],
199855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala                                        mMappedCoords[4], mMappedCoords[5],
200855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala                                        mMappedCoords[8], mMappedCoords[9],
201855b25acc20d38dfb98eff8bf73fbc441e174a92Eino-Ville Talvala                                        mMappedCoords[12], mMappedCoords[13]);
2024a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
2034a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        Frame output = context.getFrameManager().newFrame(mOutputFormat);
2044a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mFrameExtractor.process(mCameraFrame, output);
2054a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
2068dd704358d808382465666354fc891af59b21e18Eino-Ville Talvala        long timestamp = mSurfaceTexture.getTimestamp();
2078dd704358d808382465666354fc891af59b21e18Eino-Ville Talvala        if (mLogVerbose) Log.v(TAG, "Timestamp: " + (timestamp / 1000000000.0) + " s");
2088dd704358d808382465666354fc891af59b21e18Eino-Ville Talvala        output.setTimestamp(timestamp);
2098dd704358d808382465666354fc891af59b21e18Eino-Ville Talvala
21021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        pushOutput("video", output);
2114a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
2124a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        // Release pushed frame
2134a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        output.release();
2144a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
21557774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        if (mLogVerbose) Log.v(TAG, "Done processing new frame");
2164a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
2174a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
2184a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    @Override
2194a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    public void close(FilterContext context) {
22057774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        if (mLogVerbose) Log.v(TAG, "Closing");
2214a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
2224a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mCamera.release();
22324455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala        mCamera = null;
224aae596d15186de172632499692d44bc5436108f7Eino-Ville Talvala        mSurfaceTexture.release();
2254a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        mSurfaceTexture = null;
2264a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
2274a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
2284a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    @Override
2294a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    public void tearDown(FilterContext context) {
2304a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        if (mCameraFrame != null) {
2314a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            mCameraFrame.release();
2324a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        }
2334a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
2344a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
2354a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    @Override
23621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void fieldPortValueUpdated(String name, FilterContext context) {
23721d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        if (name.equals("framerate")) {
23824455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            getCameraParameters();
23924455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            int closestRange[] = findClosestFpsRange(mFps, mCameraParameters);
24024455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            mCameraParameters.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
24124455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala                                                 closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
24224455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            mCamera.setParameters(mCameraParameters);
2434a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        }
2444a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
2454a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
24624455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala    synchronized public Camera.Parameters getCameraParameters() {
2479aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        boolean closeCamera = false;
24824455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala        if (mCameraParameters == null) {
24924455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            if (mCamera == null) {
25024455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala                mCamera = Camera.open(mCameraId);
25124455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala                closeCamera = true;
25224455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            }
25324455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            mCameraParameters = mCamera.getParameters();
25424455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala
25524455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            if (closeCamera) {
25624455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala                mCamera.release();
25724455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala                mCamera = null;
25824455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            }
25924455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala        }
2609aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala
2619aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        int closestSize[] = findClosestSize(mWidth, mHeight, mCameraParameters);
2629aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        mWidth = closestSize[0];
2639aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        mHeight = closestSize[1];
2649aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        mCameraParameters.setPreviewSize(mWidth, mHeight);
2659aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala
2669aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        int closestRange[] = findClosestFpsRange(mFps, mCameraParameters);
2679aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala
2689aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        mCameraParameters.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
2699aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                                             closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
2709aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala
27124455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala        return mCameraParameters;
2724a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
2734a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
2744a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    /** Update camera parameters. Image resolution cannot be changed. */
27524455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala    synchronized public void setCameraParameters(Camera.Parameters params) {
2764a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        params.setPreviewSize(mWidth, mHeight);
27724455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala        mCameraParameters = params;
27824455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala        if (isOpen()) {
27924455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala            mCamera.setParameters(mCameraParameters);
28024455ce2cc781f52c2171d0b388314393d4f7111Eino-Ville Talvala        }
2814a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
2824a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
2839aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala    private int[] findClosestSize(int width, int height, Camera.Parameters parameters) {
2849aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
2859aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        int closestWidth = -1;
2869aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        int closestHeight = -1;
2879aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        int smallestWidth = previewSizes.get(0).width;
2889aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        int smallestHeight =  previewSizes.get(0).height;
2899aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        for (Camera.Size size : previewSizes) {
2909aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            // Best match defined as not being larger in either dimension than
2919aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            // the requested size, but as close as possible. The below isn't a
2929aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            // stable selection (reording the size list can give different
2939aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            // results), but since this is a fallback nicety, that's acceptable.
294277939c4df0abdadfef5ff19b4b33480bd9a0c20Eino-Ville Talvala            if ( size.width <= width &&
295277939c4df0abdadfef5ff19b4b33480bd9a0c20Eino-Ville Talvala                 size.height <= height &&
2969aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                 size.width >= closestWidth &&
2979aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                 size.height >= closestHeight) {
2989aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                closestWidth = size.width;
2999aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                closestHeight = size.height;
3009aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            }
3019aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            if ( size.width < smallestWidth &&
3029aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                 size.height < smallestHeight) {
3039aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                smallestWidth = size.width;
3049aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                smallestHeight = size.height;
3059aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            }
3069aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        }
3079aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        if (closestWidth == -1) {
3089aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            // Requested size is smaller than any listed size; match with smallest possible
3099aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            closestWidth = smallestWidth;
3109aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            closestHeight = smallestHeight;
3119aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        }
3129aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala
3139aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        if (mLogVerbose) {
3149aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala            Log.v(TAG,
3159aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                  "Requested resolution: (" + width + ", " + height
3169aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                  + "). Closest match: (" + closestWidth + ", "
3179aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                  + closestHeight + ").");
3189aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        }
3199aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        int[] closestSize = {closestWidth, closestHeight};
3209aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        return closestSize;
3219aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala    }
3229aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala
3234a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private int[] findClosestFpsRange(int fps, Camera.Parameters params) {
3244a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
3254a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        int[] closestRange = supportedFpsRanges.get(0);
3264a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        for (int[] range : supportedFpsRanges) {
3274a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            if (range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] < fps*1000 &&
3284a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala                range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] > fps*1000 &&
3294a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala                range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] >
3304a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala                closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] &&
3314a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala                range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] <
3324a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala                closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
3334a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala                closestRange = range;
3344a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            }
3354a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        }
3369aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala        if (mLogVerbose) Log.v(TAG, "Requested fps: " + fps
3379aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                               + ".Closest frame rate range: ["
3389aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                               + closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.
3399aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                               + ","
3409aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                               + closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.
3419aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala                               + "]");
3429aeeed4a7a5ab115727a0545ea343dcc4f966851Eino-Ville Talvala
3434a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        return closestRange;
3444a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    }
3454a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
3464a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    private SurfaceTexture.OnFrameAvailableListener onCameraFrameAvailableListener =
3474a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala            new SurfaceTexture.OnFrameAvailableListener() {
34833d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua        @Override
3494a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        public void onFrameAvailable(SurfaceTexture surfaceTexture) {
35057774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala            if (mLogVerbose) Log.v(TAG, "New frame from camera");
351e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala            synchronized(CameraSource.this) {
352e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                mNewFrameAvailable = true;
353e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala                CameraSource.this.notify();
354e23bf25c468aec0e8a12746c56edcf6a8ff68aedEino-Ville Talvala            }
3554a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala        }
3564a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala    };
3574a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala
3584a0c538853d7858d59cbc7f4dc7ece5d942e0b7cEino-Ville Talvala}
359