Android培训之直接控制摄像头

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.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值