/*
* Copyright (C) 2012 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 com.example.android.rs.sto;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.SystemClock;
import android.util.Log;
import java.io.IOException;
import java.util.List;
public class CameraCapture {
public interface CameraFrameListener {
public void onNewCameraFrame();
}
static final int FRAMES_PER_SEC = 30;
private Camera mCamera;
private SurfaceTexture mSurfaceTexture;
private int mProgram;
private int mCameraTransformHandle;
private int mTexSamplerHandle;
private int mTexCoordHandle;
private int mPosCoordHandle;
private float[] mCameraTransform = new float[16];
private int mCameraId = 0;
private int mWidth;
private int mHeight;
private long mStartCaptureTime = 0;
private boolean mNewFrameAvailable = false;
private boolean mIsOpen = false;
private CameraFrameListener mListener;
public synchronized void beginCapture(int cameraId, int width, int height,
SurfaceTexture st) {
mCameraId = cameraId;
mSurfaceTexture = st;
// Open the camera
openCamera(width, height);
// Start the camera
mStartCaptureTime = SystemClock.elapsedRealtime();
mCamera.startPreview();
mIsOpen = true;
}
public void getCurrentFrame() {
if (checkNewFrame()) {
if (mStartCaptureTime > 0 && SystemClock.elapsedRealtime() - mStartCaptureTime > 2000) {
// Lock white-balance and exposure for effects
Log.i("CC", "Locking white-balance and exposure!");
Camera.Parameters params = mCamera.getParameters();
params.setAutoWhiteBalanceLock(true);
params.setAutoExposureLock(true);
//mCamera.setParameters(params);
mStartCaptureTime = 0;
}
mSurfaceTexture.updateTexImage();
mSurfaceTexture.getTransformMatrix(mCameraTransform);
// display it here
}
}
public synchronized boolean hasNewFrame() {
return mNewFrameAvailable;
}
public synchronized void endCapture() {
mIsOpen = false;
if (mCamera != null) {
mCamera.release();
mCamera = null;
mSurfaceTexture = null;
}
}
public synchronized boolean isOpen() {
return mIsOpen;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
public void setCameraFrameListener(CameraFrameListener listener) {
mListener = listener;
}
private void openCamera(int width, int height) {
// Setup camera
mCamera = Camera.open(mCameraId);
mCamera.setParameters(calcCameraParameters(width, height));
// Create camera surface texture
try {
mCamera.setPreviewTexture(mSurfaceTexture);
} catch (IOException e) {
throw new RuntimeException("Could not bind camera surface texture: " +
e.getMessage() + "!");
}
// Connect SurfaceTexture to callback
mSurfaceTexture.setOnFrameAvailableListener(onCameraFrameAvailableListener);
}
private Camera.Parameters calcCameraParameters(int width, int height) {
Camera.Parameters params = mCamera.getParameters();
params.setPreviewSize(mWidth, mHeight);
// Find closest size
int closestSize[] = findClosestSize(width, height, params);
mWidth = closestSize[0];
mHeight = closestSize[1];
params.setPreviewSize(mWidth, mHeight);
// Find closest FPS
int closestRange[] = findClosestFpsRange(FRAMES_PER_SEC, params);
params.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
return params;
}
private int[] findClosestSize(int width, int height, Camera.Parameters parameters) {
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
int closestWidth = -1;
int closestHeight = -1;
int smallestWidth = previewSizes.get(0).width;
int smallestHeight = previewSizes.get(0).height;
for (Camera.Size size : previewSizes) {
// Best match defined as not being larger in either dimension than
// the requested size, but as close as possible. The below isn't a
// stable selection (reording the size list can give different
// results), but since this is a fallback nicety, that's acceptable.
if ( size.width <= width &&
size.height <= height &&
size.width >= closestWidth &&
size.height >= closestHeight) {
closestWidth = size.width;
closestHeight = size.height;
}
if ( size.width < smallestWidth &&
size.height < smallestHeight) {
smallestWidth = size.width;
smallestHeight = size.height;
}
}
if (closestWidth == -1) {
// Requested size is smaller than any listed size; match with smallest possible
closestWidth = smallestWidth;
closestHeight = smallestHeight;
}
int[] closestSize = {closestWidth, closestHeight};
return closestSize;
}
private int[] findClosestFpsRange(int fps, Camera.Parameters params) {
List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
int[] closestRange = supportedFpsRanges.get(0);
int fpsk = fps * 1000;
int minDiff = 1000000;
for (int[] range : supportedFpsRanges) {
int low = range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
int high = range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
if (low <= fpsk && high >= fpsk) {
int diff = (fpsk - low) + (high - fpsk);
if (diff < minDiff) {
closestRange = range;
minDiff = diff;
}
}
}
Log.i("CC", "Found closest range: "
+ closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + " - "
+ closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
return closestRange;
}
private synchronized void signalNewFrame() {
mNewFrameAvailable = true;
if (mListener != null) {
mListener.onNewCameraFrame();
}
}
private synchronized boolean checkNewFrame() {
if (mNewFrameAvailable) {
mNewFrameAvailable = false;
return true;
}
return false;
}
private SurfaceTexture.OnFrameAvailableListener onCameraFrameAvailableListener =
new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
signalNewFrame();
}
};
}