您的位置:首页 > 移动开发 > Android开发

Android培训之直接控制摄像头

2014-05-14 16:45 211 查看
http://wiki.heyitao.com/program/android/training/camera-cameradirect

这个课时将会讨论如何通过系统的 APIs 来直接控制摄像头。

和使用相机程序拍照和录制视频比起来,直接控制摄像头要比较麻烦一点点。如果您想开发一个专业的相机程序或者完全自定义拍照的界面,这个课时将告诉你如何去做。

你也可以参考开发指南里的摄像头章节学习更多的知识。


打开摄像头

在控制摄像头之前,需要先获取到 Camera 对象。和Android自带的相机程序一样,推荐在另外一个线程中打开摄像头。 打开摄像头可能需要消耗一些时间,放到非UI线程中就不会出现ANR了。一般可以在 onResume() 函数中启动打开摄像头的线程。

如果摄像头正在被其他程序使用,那么调用 Camera.open() 会抛出一个异常,所以要用 try 来处理这个异常。
private boolean safeCameraOpen(int id) {
boolean qOpened = false;

try {
releaseCameraAndPreview();
mCamera = Camera.open(id);
qOpened = (mCamera != null);
} catch (Exception e) {
Log.e(getString(R.string.app_name), "failed to open Camera");
e.printStackTrace();
}

return qOpened;
}

private void releaseCameraAndPreview() {
mPreview.setCamera(null);
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}


从 API level 9 开始,框架支持多个摄像头了。如果使用旧的API, 不设置任何参数调用函数 open() 将会打开第一个背面的摄像头。


创建 Camera Preview

拍照程序通常在用户按下快门之前显示一个预览窗口。要实现这个预览窗口需要使用 SurfaceView 对象。


Preview Class

要显示一个预览图像就需要一个预览的类。该类需要实现 android.view.SurfaceHolder.Callback 接口,该接口用来把摄像头获取的数据传递给预览窗口显示。
class Preview extends ViewGroup implements SurfaceHolder.Callback {

SurfaceView mSurfaceView;
SurfaceHolder mHolder;

Preview(Context context) {
super(context);

mSurfaceView = new SurfaceView(context);
addView(mSurfaceView);

// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
...
}


在显示预览图像之前,需要把这个预览类设置到 Camera 对象中,下面会介绍如何设置。


设置预览类开始显示预览窗口

摄像头对象和预览类必须按照特定的顺序来创建,要首先创建摄像头对象。下面的代码片段中,初始化摄像头的工作封装起来了,用户每次修改了摄像头的设置,然后调用setCamera()函数时都会调用函数 Camera.startPreview() 。每次在预览类的 surfaceChanged() 回调函数中都需要重新启动预览。
public void setCamera(Camera camera) {
if (mCamera == camera) { return; }

stopPreviewAndFreeCamera();

mCamera = camera;

if (mCamera != null) {
List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
mSupportedPreviewSizes = localSizes;
requestLayout();

try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
e.printStackTrace();
}

/*
Important: Call startPreview() to start updating the preview surface. Preview must
be started before you can take a picture.
*/
mCamera.startPreview();
}
}



修改摄像头配置信息

通过配置可以修改摄像头的一些参数,比如聚焦和曝光补偿等。下面的示例代码只是修改了预览的大小,更多详细控制请参考相机程序。
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);

/*
Important: Call startPreview() to start updating the preview surface. Preview must be
started before you can take a picture.
*/
mCamera.startPreview();
}



设置预览窗口的方向

大多数的相机程序都使用横向拍照,这也是摄像头传感器的自然方向。但是这并不影响您在竖屏的时候拍照,设备的方向信息会存储到图片的EXIF信息中。可以通过函数 setCameraDisplayOrientation() 来改变预览的显示方向而不影响图片的保存数据。然而,在API level 14之前的版本中,在修改预览方向之前需要先停止预览窗口然后重新启动预览。


拍摄照片

在预览启动以后就可以通过函数 Camera.takePicture() 来拍摄照片了。可以创建 Camera.PictureCallback 和 Camera.ShutterCallback 对象作为参数来调用函数 Camera.takePicture()。

如果您想连续不断的拍照,可以创建一个实现 Camera.PreviewCallback 接口的类并且实现 onPreviewFrame() 函数。 For something in between, you can capture only selected preview frames, or set up a delayed action to call takePicture().


重启预览窗口

在拍摄一张照片后,需要重启预览来能继续拍摄下一张。下面的示例在处理快门事件的时候重启了预览。
@Override
public void onClick(View v) {
switch(mPreviewState) {
case K_STATE_FROZEN:
mCamera.startPreview();
mPreviewState = K_STATE_PREVIEW;
break;

default:
mCamera.takePicture( null, rawCallback, null);
mPreviewState = K_STATE_BUSY;
} // switch
shutterBtnConfig();
}



停止预览并且释放资源

何时停止预览并释放摄像头呢? 当预览surface被销毁的时候去释放资源是个不错的地方,下面是在 Preview 类中的一个示例代码:
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
if (mCamera != null) {
/*
Call stopPreview() to stop updating the preview surface.
*/
mCamera.stopPreview();
}
}

/**
* When this function returns, mCamera will be null.
*/
private void stopPreviewAndFreeCamera() {

if (mCamera != null) {
/*
Call stopPreview() to stop updating the preview surface.
*/
mCamera.stopPreview();

/*
Important: Call release() to release the camera for use by other applications.
Applications should release the camera immediately in onPause() (and re-open() it in
onResume()).
*/
mCamera.release();

mCamera = null;
}
}


Earlier in the lesson, this procedure was also part of the setCamera() method, so initializing a camera always begins with stopping the preview.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: