1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package com.example.android.rs.sto;
19
20import android.graphics.SurfaceTexture;
21import android.hardware.Camera;
22import android.os.SystemClock;
23import android.util.Log;
24
25import java.io.IOException;
26import java.util.List;
27
28public class CameraCapture {
29
30    public interface CameraFrameListener {
31        public void onNewCameraFrame();
32    }
33
34    static final int FRAMES_PER_SEC = 30;
35
36    private Camera mCamera;
37    private SurfaceTexture mSurfaceTexture;
38
39    private int mProgram;
40
41    private int mCameraTransformHandle;
42    private int mTexSamplerHandle;
43    private int mTexCoordHandle;
44    private int mPosCoordHandle;
45
46    private float[] mCameraTransform = new float[16];
47
48    private int mCameraId = 0;
49    private int mWidth;
50    private int mHeight;
51
52    private long mStartCaptureTime = 0;
53
54    private boolean mNewFrameAvailable = false;
55    private boolean mIsOpen = false;
56
57    private CameraFrameListener mListener;
58
59    public synchronized void beginCapture(int cameraId, int width, int height,
60                                          SurfaceTexture st) {
61        mCameraId = cameraId;
62        mSurfaceTexture = st;
63
64        // Open the camera
65        openCamera(width, height);
66
67        // Start the camera
68        mStartCaptureTime = SystemClock.elapsedRealtime();
69        mCamera.startPreview();
70        mIsOpen = true;
71    }
72
73    public void getCurrentFrame() {
74        if (checkNewFrame()) {
75            if (mStartCaptureTime > 0 && SystemClock.elapsedRealtime() - mStartCaptureTime > 2000) {
76                // Lock white-balance and exposure for effects
77                Log.i("CC", "Locking white-balance and exposure!");
78                Camera.Parameters params = mCamera.getParameters();
79                params.setAutoWhiteBalanceLock(true);
80                params.setAutoExposureLock(true);
81                //mCamera.setParameters(params);
82                mStartCaptureTime = 0;
83            }
84
85            mSurfaceTexture.updateTexImage();
86            mSurfaceTexture.getTransformMatrix(mCameraTransform);
87
88            // display it here
89        }
90    }
91
92    public synchronized boolean hasNewFrame() {
93        return mNewFrameAvailable;
94    }
95
96    public synchronized void endCapture() {
97        mIsOpen = false;
98        if (mCamera != null) {
99            mCamera.release();
100            mCamera = null;
101            mSurfaceTexture = null;
102        }
103    }
104
105    public synchronized boolean isOpen() {
106        return mIsOpen;
107    }
108
109    public int getWidth() {
110        return mWidth;
111    }
112
113    public int getHeight() {
114        return mHeight;
115    }
116
117    public void setCameraFrameListener(CameraFrameListener listener) {
118        mListener = listener;
119    }
120
121    private void openCamera(int width, int height) {
122        // Setup camera
123        mCamera = Camera.open(mCameraId);
124        mCamera.setParameters(calcCameraParameters(width, height));
125
126        // Create camera surface texture
127        try {
128            mCamera.setPreviewTexture(mSurfaceTexture);
129        } catch (IOException e) {
130            throw new RuntimeException("Could not bind camera surface texture: " +
131                                       e.getMessage() + "!");
132        }
133
134        // Connect SurfaceTexture to callback
135        mSurfaceTexture.setOnFrameAvailableListener(onCameraFrameAvailableListener);
136    }
137
138    private Camera.Parameters calcCameraParameters(int width, int height) {
139        Camera.Parameters params = mCamera.getParameters();
140        params.setPreviewSize(mWidth, mHeight);
141
142        // Find closest size
143        int closestSize[] = findClosestSize(width, height, params);
144        mWidth = closestSize[0];
145        mHeight = closestSize[1];
146        params.setPreviewSize(mWidth, mHeight);
147
148        // Find closest FPS
149        int closestRange[] = findClosestFpsRange(FRAMES_PER_SEC, params);
150
151        params.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
152                                  closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
153
154        return params;
155    }
156
157    private int[] findClosestSize(int width, int height, Camera.Parameters parameters) {
158        List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
159        int closestWidth = -1;
160        int closestHeight = -1;
161        int smallestWidth = previewSizes.get(0).width;
162        int smallestHeight =  previewSizes.get(0).height;
163        for (Camera.Size size : previewSizes) {
164            // Best match defined as not being larger in either dimension than
165            // the requested size, but as close as possible. The below isn't a
166            // stable selection (reording the size list can give different
167            // results), but since this is a fallback nicety, that's acceptable.
168            if ( size.width <= width &&
169                 size.height <= height &&
170                 size.width >= closestWidth &&
171                 size.height >= closestHeight) {
172                closestWidth = size.width;
173                closestHeight = size.height;
174            }
175            if ( size.width < smallestWidth &&
176                 size.height < smallestHeight) {
177                smallestWidth = size.width;
178                smallestHeight = size.height;
179            }
180        }
181        if (closestWidth == -1) {
182            // Requested size is smaller than any listed size; match with smallest possible
183            closestWidth = smallestWidth;
184            closestHeight = smallestHeight;
185        }
186        int[] closestSize = {closestWidth, closestHeight};
187        return closestSize;
188    }
189
190    private int[] findClosestFpsRange(int fps, Camera.Parameters params) {
191        List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
192        int[] closestRange = supportedFpsRanges.get(0);
193        int fpsk = fps * 1000;
194        int minDiff = 1000000;
195        for (int[] range : supportedFpsRanges) {
196            int low = range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
197            int high = range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
198            if (low <= fpsk && high >= fpsk) {
199                int diff = (fpsk - low) + (high - fpsk);
200                if (diff < minDiff) {
201                    closestRange = range;
202                    minDiff = diff;
203                }
204            }
205        }
206        Log.i("CC", "Found closest range: "
207            + closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + " - "
208            + closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
209        return closestRange;
210    }
211
212    private synchronized void signalNewFrame() {
213        mNewFrameAvailable = true;
214        if (mListener != null) {
215            mListener.onNewCameraFrame();
216        }
217    }
218
219    private synchronized boolean checkNewFrame() {
220        if (mNewFrameAvailable) {
221            mNewFrameAvailable = false;
222            return true;
223        }
224        return false;
225    }
226
227    private SurfaceTexture.OnFrameAvailableListener onCameraFrameAvailableListener =
228            new SurfaceTexture.OnFrameAvailableListener() {
229        @Override
230        public void onFrameAvailable(SurfaceTexture surfaceTexture) {
231            signalNewFrame();
232        }
233    };
234}
235