Camera API

1、清单声明

摄像机权限 - 您的应用程序必须请求使用设备摄像头的权限。
<uses-permission android:name =“android.permission.CAMERA”/>
注意:如果您通过调用现有的相机应用程序来使用相机,则您的应用程序无需请求此权限。

相机功能 - 您的应用程序还必须声明使用相机功能,例如:
<uses-feature android:name =“android.hardware.camera”/>

存储权限 - 如果您的应用程序将图像或视频保存到设备的外部存储(SD卡),则还必须在清单中指定。
<uses-permission android:name =“android.permission.WRITE_EXTERNAL_STORAGE”/>


录音许可 - 要录制带视频捕捉的音频,您的应用必须申请音频捕获许可。
<uses-permission android:name =“android.permission.RECORD_AUDIO”/>


位置权限 - 如果您的应用程序使用GPS位置信息标记图像,则必须请求ACCESS_FINE_LOCATION权限。请注意,如果您的应用针对Android 5.0(API级别21)或更高级别,您还需要声明您的应用使用设备的GPS:

<uses-permission android:name =“android.permission.ACCESS_FINE_LOCATION”/>

2、创建自定义相机界面

一般步骤如下:

  检测和访问摄像头 - 创建代码以检查摄像头是否存在并请求访问。
  创建预览类 - 创建一个扩展SurfaceView并实现SurfaceHolder接口的相机预览类。本课程预览来自相机的实时图像。
  构建预览布局 - 获得相机预览类后,创建一个包含预览和所需用户界面控件的视图布局。
  设置Capture的监听器 - 连接接口控件的监听器以启动图像或视频捕获以响应用户操作,例如按下按钮。
  捕获和保存文件 - 设置捕获图片或视频的代码并保存输出。
  释放相机 - 使用相机后,您的应用程序必须正确释放它以供其他应用程序使用。


相机硬件是必须谨慎管理的共享资源,因此您的应用程序不会与可能也想要使用它的其他应用程序发生冲突。以下部分讨论如何检测相机硬件,如何请求访问相机,如何捕获图片或视频以及如何在使用应用程序时释放相机。

注意:请记住在使用应用程序完成后调用Camera.release()来释放Camera对象!如果您的应用程序未正确释放相机,则所有后续访问相机的尝试(包括您自己的应用程序)都将失败,并可能导致您或其他应用程序关闭。

3、检测相机硬件

  如果您的应用程序没有特别要求使用清单声明的摄像头,则应检查运行时是否有可用的摄像头。 要执行此检查,请使用PackageManager.hasSystemFeature()方法,如下面的示例代码所示:

/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        // this device has a camera
        return true;
    } else {
        // no camera on this device
        return false;
    }
}

Android设备可以有多个摄像头,例如用于摄影的后置摄像头和用于视频呼叫的前置摄像头。 Android 2.3(API级别9)及更高版本允许您使用Camera.getNumberOfCameras()方法检查设备上可用的摄

像机数量。

4、访问相机

  如果您确定运行应用程序的设备有摄像头,则必须通过获取摄像头实例来请求访问它(除非您使用的是访问摄像头的意图)。要访问主摄像头,请使用Camera.open()方法并确保捕获任何异常,如下面的代码所示:

/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
    Camera c = null;
    try {
        c = Camera.open(); // attempt to get a Camera instance
    }
    catch (Exception e){
        // Camera is not available (in use or does not exist)
    }
    return c; // returns null if camera is unavailable
}

在运行Android 2.3(API级别9)或更高版本的设备上,您可以使用Camera.open(int)访问特定的摄像头。 上面的示例代码将访问具有多个摄像头的设备上的第一个后置摄像头。

5、检查相机功能

  获得对摄像头的访问权限后,您可以使用Camera.getParameters()方法获取有关其功能的更多信息,并检查返回的Camera.Parameters对象以获取支持的功能。 使用API级别9或更高级别时,请使用Camera.getCameraInfo()来确定相机是在设备的正面还是背面,以及图像的方向。

6、创建预览类

  为了让用户有效地拍摄照片或视频,他们必须能够看到设备相机看到的内容。 相机预览类是SurfaceView,可以显示来自相机的实时图像数据,因此用户可以构图和捕获图片或视频。

  以下示例代码演示了如何创建可包含在View布局中的基本相机预览类。 此类实现SurfaceHolder.Callback以捕获用于创建和销毁视图的回调事件,这些事件是分配摄像头预览输入所必需的。

/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (mHolder.getSurface() == null){
          // preview surface does not exist
          return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
          // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

如果要为相机预览设置特定大小,请在surfaceChanged()方法中进行设置,如上面的注释中所述。 设置预览大小时,必须使用getSupportedPreviewSizes()中的值。 不要在setPreviewSize()方法中设置任意值。

7、在布局中放置预览摄像机预览类(例如上一节中显示的示例)必须与活动布局一起放置在用于拍摄照片或视频的其他用户界面控件中。 本节介绍如何为预览构建基本布局和活动。以下布局代码提供了一个非常基本的视图,可用于显示相机预览。 在此示例中,FrameLayout元素旨在用作相机预览类的容器。 使用此布局类型,以便可以在实时相机预览图像上覆盖附加图片信息或控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    />

  <Button
    android:id="@+id/button_capture"
    android:text="Capture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>

在大多数设备上,相机预览的默认方向是横向。 此示例布局指定水平(横向)布局,下面的代码将应用程序的方向固定为横向。 为简化渲染相机预览,您应将应用程序的预览活动方向更改为横向,方法是将以下内容添加到清单中。

<activity android:name=".CameraActivity"
          android:label="@string/app_name"

          android:screenOrientation="landscape">
          <!-- configure this activity to use landscape orientation -->

          <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

注意:相机预览不必处于横向模式。 从Android 2.2(API级别8)开始,您可以使用setDisplayOrientation()方法设置预览图像的旋转。 为了在用户重新定位手机时更改预览方向,在预览类的surfaceChanged()方法中,首先使用Camera.stopPreview()停止预览,更改方向,然后使用Camera.startPreview再次开始预览()。

在摄像机视图的活动中,将预览类添加到上面示例中显示的FrameLayout元素。 您的相机活动还必须确保在相机暂停或关闭时释放相机。 以下示例显示如何修改相机活动以附加创建预览类中显示的预览类。

public class CameraActivity extends Activity {

    private Camera mCamera;
    private CameraPreview mPreview;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Create an instance of Camera
        mCamera = getCameraInstance();

        // Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }
}

8、捕捉图片

一旦构建了预览类和显示它的视图布局,就可以开始使用应用程序捕获图像了。 在应用程序代码中,必须为用户界面控件设置侦听器,以通过拍照来响应用户操作。要检索图片,请使用Camera.takePicture()方法。 该方法采用三个从摄像机接收数据的参数。 要以JPEG格式接收数据,必须实现Camera.PictureCallback接口以接收图像数据并将其写入文件。 以下代码显示了Camera.PictureCallback接口的基本实现,用于保存从相机接收的图像。

private PictureCallback mPicture = new PictureCallback() {

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {

        File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
        if (pictureFile == null){
            Log.d(TAG, "Error creating media file, check storage permissions");
            return;
        }

        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
        }
    }
};

触发通过调用Camera.takePicture()方法捕获图像。 以下示例代码显示如何从按钮View.OnClickListener调用此方法

// Add a listener to the Capture button
Button captureButton = (Button) findViewById(R.id.button_capture);
captureButton.setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // get an image from the camera
            mCamera.takePicture(null, null, mPicture);
        }
    }
);

9、捕捉视频

使用Android框架进行视频捕获需要仔细管理Camera对象并与MediaRecorder类协调。 使用Camera录制视频时,除了Camera.open()和Camera.release()调用之外,您还必须管理Camera.lock()和Camera.unlock()调用以允许MediaRecorder访问摄像头硬件。

注意:从Android 4.0(API级别14)开始,将自动为您管理Camera.lock()和Camera.unlock()调用。

与使用设备相机拍照不同,捕捉视频需要非常特殊的通话顺序。您必须遵循特定的执行顺序才能成功准备并使用您的应用程序捕获视频,详情如下。

打开相机 - 使用Camera.open()获取相机对象的实例。
连接预览 - 使用Camera.setPreviewDisplay()将SurfaceView连接到摄像机,准备实时摄像机图像预览。
开始预览 - 调用Camera.startPreview()开始显示实时摄像机图像。
开始录制视频 - 必须完成以下步骤才能成功录制视频:
解锁相机 - 通过调用Camera.unlock()解锁相机以供MediaRecorder使用。
配置MediaRecorder - 按此顺序调用以下MediaRecorder方法。有关更多信息,请参阅MediaRecorder参考文档。
setCamera() - 设置要用于视频捕获的摄像头,使用应用程序的当前Camera实例。
setAudioSource() - 设置音频源,使用MediaRecorder.AudioSource.CAMCORDER。
setVideoSource() - 设置视频源,使用MediaRecorder.VideoSource.CAMERA。
设置视频输出格式和编码。对于Android 2.2(API Level 8)及更高版本,请使用MediaRecorder.setProfile方法,并使用CamcorderProfile.get()获取配置文件实例。对于2.2之前的Android版本,您必须设置视频输出格式和编码参数:
setOutputFormat() - 设置输出格式,指定默认设置或MediaRecorder.OutputFormat.MPEG_4。
setAudioEncoder() - 设置声音编码类型,指定默认设置或MediaRecorder.AudioEncoder.AMR_NB。
setVideoEncoder() - 设置视频编码类型,指定默认设置或MediaRecorder.VideoEncoder.MPEG_4_SP。
setOutputFile() - 设置输出文件,使用“保存媒体文件”部分中的示例方法中的getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()。
setPreviewDisplay() - 为应用程序指定SurfaceView预览布局元素。使用为Connect Preview指定的相同对象。
警告:您必须按此顺序调用这些MediaRecorder配置方法,否则您的应用程序将遇到错误并且录制将失败。

准备MediaRecorder - 通过调用MediaRecorder.prepare(),使用提供的配置设置准备MediaRecorder。
启动MediaRecorder - 通过调用MediaRecorder.start()开始录制视频。
停止录制视频 - 按顺序调用以下方法,以成功完成视频录制:
停止MediaRecorder - 通过调用MediaRecorder.stop()停止录制视频。
重置MediaRecorder - 可选择通过调用MediaRecorder.reset()从记录器中删除配置设置。
Release MediaRecorder - 通过调用MediaRecorder.release()释放MediaRecorder。
锁定摄像头 - 锁定摄像头,以便将来的MediaRecorder会话可以通过调用Camera.lock()来使用它。从Android 4.0(API级别14)开始,除非MediaRecorder.prepare()调用失败,否则不需要此调用。
停止预览 - 当您的活动使用完相机后,使用Camera.stopPreview()停止预览。
释放相机 - 释放相机,以便其他应用程序可以通过调用Camera.release()来使用它。

注意:可以在不先创建摄像头预览的情况下使用MediaRecorder,并跳过此过程的前几个步骤。 但是,由于用户通常更喜欢在开始录制之前看到预览,因此这里不讨论该过程。

提示:如果您的应用程序通常用于录制视频,请在开始预览之前将setRecordingHint(boolean)设置为true。 此设置有助于减少开始录制所需的时间。

10、配置MediaRecorder

  使用MediaRecorder类记录视频时,必须按特定顺序执行配置步骤,然后调用MediaRecorder.prepare()方法检查并实施配置。 以下示例代码演示了如何正确配置和准备MediaRecorder类以进行视频录制。

private boolean prepareVideoRecorder(){

    mCamera = getCameraInstance();
    mMediaRecorder = new MediaRecorder();

    // Step 1: Unlock and set camera to MediaRecorder
    mCamera.unlock();
    mMediaRecorder.setCamera(mCamera);

    // Step 2: Set sources
    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

    // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
    mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));

    // Step 4: Set output file
    mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());

    // Step 5: Set the preview output
    mMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());

    // Step 6: Prepare configured MediaRecorder
    try {
        mMediaRecorder.prepare();
    } catch (IllegalStateException e) {
        Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
        releaseMediaRecorder();
        return false;
    } catch (IOException e) {
        Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());
        releaseMediaRecorder();
        return false;
    }
    return true;
}

在Android 2.2(API Level 8)之前,您必须直接设置输出格式和编码格式参数,而不是使用CamcorderProfile。 以下代码演示了此方法:

    // Step 3: Set output format and encoding (for versions prior to API Level 8)
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);

MediaRecorder的以下视频录制参数是默认设置,但是,您可能需要为应用程序调整这些设置:

setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()

setAudioSamplingRate()

11、启动和停止MediaRecorder

使用MediaRecorder类开始和停止视频录制时,您必须遵循特定的顺序,如下所示。

使用Camera.unlock()解锁相机
配置MediaRecorder,如上面的代码示例所示
使用MediaRecorder.start()开始录制
录制视频
使用MediaRecorder.stop()停止录制
使用MediaRecorder.release()释放介质记录器
使用Camera.lock()锁定相机
以下示例代码演示了如何使用相机和MediaRecorder类连接按钮以正确启动和停止视频录制。

注意:完成视频录制时,请勿释放相机,否则将停止预览。

private boolean isRecording = false;

// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (isRecording) {
                // stop recording and release camera
                mMediaRecorder.stop();  // stop the recording
                releaseMediaRecorder(); // release the MediaRecorder object
                mCamera.lock();         // take camera access back from MediaRecorder

                // inform the user that recording has stopped
                setCaptureButtonText("Capture");
                isRecording = false;
            } else {
                // initialize video camera
                if (prepareVideoRecorder()) {
                    // Camera is available and unlocked, MediaRecorder is prepared,
                    // now you can start recording
                    mMediaRecorder.start();

                    // inform the user that recording has started
                    setCaptureButtonText("Stop");
                    isRecording = true;
                } else {
                    // prepare didn't work, release the camera
                    releaseMediaRecorder();
                    // inform user
                }
            }
        }
    }
);

 注意:在上面的示例中,prepareVideoRecorder()方法引用配置MediaRecorder中显示的示例代码。 此方法负责锁定摄像头,配置和准备MediaRecorder实例。

12、释放相机

摄像头是设备上的应用程序共享的资源。 在获取Camera实例后,您的应用程序可以使用相机,并且在应用程序停止使用相机时,一旦应用程序暂停(Activity.onPause()),您必须特别小心地释放相机对象。 如果您的应用程序未正确释放相机,则所有后续访问相机的尝试(包括您自己的应用程序)都将失败,并可能导致您或其他应用程序关闭。

要释放Camera对象的实例,请使用Camera.release()方法,如下面的示例代码所示。

public class CameraActivity extends Activity {
    private Camera mCamera;
    private SurfaceView mPreview;
    private MediaRecorder mMediaRecorder;

    ...

    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    private void releaseMediaRecorder(){
        if (mMediaRecorder != null) {
            mMediaRecorder.reset();   // clear recorder configuration
            mMediaRecorder.release(); // release the recorder object
            mMediaRecorder = null;
            mCamera.lock();           // lock camera for later use
        }
    }

    private void releaseCamera(){
        if (mCamera != null){
            mCamera.release();        // release the camera for other applications
            mCamera = null;
        }
    }
}

13、保存媒体文件

用户创建的媒体文件(如图片和视频)应保存到设备的外部存储目录(SD卡)中,以节省系统空间,并允许用户在没有设备的情况下访问这些文件。有许多可能的目录位置可以在设备上保存媒体文件,但是作为开发人员,应该考虑两个标准位置:

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) - 此方法返回用于保存图片和视频的标准,共享和推荐位置。此目录是共享的(公共),因此其他应用程序可以轻松发现,读取,更改和删除保存在此位置的文件。如果用户卸载了您的应用程序,则不会删除保存到此位置的媒体文件。为避免干扰用户现有的图片和视频,您应该在此目录中为应用程序的媒体文件创建一个子目录,如下面的代码示例所示。此方法在Android 2.2(API级别8)中可用,对于早期API版本中的等效调用,请参阅保存共享文件。
Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - 此方法返回一个标准位置,用于保存与您的应用程序关联的图片和视频。如果卸载了您的应用程序,则会删除此位置中保存的所有文件。不对此位置的文件强制执行安全性,其他应用程序可能会读取,更改和删除它们。
以下示例代码演示了如何为媒体文件创建文件或Uri位置,该文​​件可以在使用Intent调用设备的摄像头时或作为构建摄像头应用程序的一部分时使用

public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;

/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type){
      return Uri.fromFile(getOutputMediaFile(type));
}

/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this.

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
              Environment.DIRECTORY_PICTURES), "MyCameraApp");
    // This location works best if you want the created images to be shared
    // between applications and persist after your app has been uninstalled.

    // Create the storage directory if it does not exist
    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }
    }

    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    if (type == MEDIA_TYPE_IMAGE){
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
        "IMG_"+ timeStamp + ".jpg");
    } else if(type == MEDIA_TYPE_VIDEO) {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
        "VID_"+ timeStamp + ".mp4");
    } else {
        return null;
    }

    return mediaFile;
}

14、相机功能
Android支持您可以使用相机应用程序控制的各种相机功能,例如图片格式,闪光模式,对焦设置等等。 本节列出了常见的相机功能,并简要讨论了如何使用它们。 可以使用through Camera.Parameters对象访问和设置大多数相机功能。 但是,有几个重要功能需要的不仅仅是Camera.Parameters中的简单设置。 以下各节介绍了这些功能:

    计量和聚焦区域
    人脸检测
    时间流逝的视频


有关如何使用通过Camera.Parameters控制的功能的一般信息,请查看使用相机功能部分。 有关如何使用通过摄像机参数对象控制的功能的更多详细信息,请按照下面功能列表中的链接访问API参考文档。

15、检查功能可用性
在Android设备上开始使用相机功能时,首先要了解的是并非所有设备都支持所有相机功能。 此外,支持特定功能的设备可以支持不同级别或不同选项。 因此,在开发相机应用程序时,您的决策过程的一部分是决定您想要支持哪些相机功能以及达到什么级别。 做出决定后,您应该计划在相机应用程序中包含代码,以检查设备硬件是否支持这些功能,如果功能不可用则优先失败。

您可以通过获取摄像机参数对象的实例并检查相关方法来检查摄像机功能的可用性。 以下代码示例演示如何获取Camera.Parameters对象并检查相机是否支持自动对焦功能:

// get Camera parameters
Camera.Parameters params = mCamera.getParameters();

List<String> focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
  // Autofocus mode is supported
}

16、使用相机功能
使用Camera.Parameters对象激活和控制大多数相机功能。 通过首先获取Camera对象的实例,调用getParameters()方法,更改返回的参数对象,然后将其设置回相机对象,获得此对象,如以下示例代码所示:

// get Camera parameters
Camera.Parameters params = mCamera.getParameters();
// set the focus mode
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
// set Camera parameters
mCamera.setParameters(params);

此技术适用于几乎所有相机功能,并且在获取Camera对象的实例后,可以随时更改大多数参数。 在应用程序的相机预览中,用户通常可以看到对参数的更改。 在软件方面,当相机硬件处理新指令然后发送更新的图像数据时,参数改变可能需要几帧才能实际生效。

重要提示:某些相机功能无法随意更改。 特别是,更改相机预览的大小或方向需要先停止预览,更改预览大小,然后重新开始预览。 从Android 4.0(API级别14)开始,可以在不重新开始预览的情况下更改预览方向。

其他相机功能需要更多代码才能实现,包括:

  计量和聚焦区域
  人脸检测
  时间流逝的视频
以下各节提供了有关如何实现这些功能的快速概述。

17、计量和聚焦区域
在某些拍摄场景中,自动对焦和测光可能无法产生预期效果。 从Android 4.0(API Level 14)开始,您的相机应用程序可以提供其他控件,以允许您的应用或用户指定图像中用于确定焦点或光照级别设置的区域,并将这些值传递给相机硬件以用于捕获 图像或视频。

计量和聚焦的区域与其他相机功能非常相似,您可以通过Camera.Parameters对象中的方法控制它们。 以下代码演示了为Camera实例设置两个测光区域:

// Create an instance of Camera
mCamera = getCameraInstance();

// set Camera parameters
Camera.Parameters params = mCamera.getParameters();

if (params.getMaxNumMeteringAreas() > 0){ // check that metering areas are supported
    List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();

    Rect areaRect1 = new Rect(-100, -100, 100, 100);    // specify an area in center of image
    meteringAreas.add(new Camera.Area(areaRect1, 600)); // set weight to 60%
    Rect areaRect2 = new Rect(800, -1000, 1000, -800);  // specify an area in upper right of image
    meteringAreas.add(new Camera.Area(areaRect2, 400)); // set weight to 40%
    params.setMeteringAreas(meteringAreas);
}

mCamera.setParameters(params);

Camera.Area对象包含两个数据参数:一个Rect对象,用于指定摄像机视野内的区域和一个权重值,它告诉摄像机在测光或焦点计算中应该给出该区域的重要程度。

Camera.Area对象中的Rect字段描述了映射在2000 x 2000单位网格上的矩形形状。 坐标-1000,-1000表示摄像机图像的左上角,坐标1000,1000表示摄像机图像的右下角,如下图所示。

图1.红线表示用于在摄像机预览中指定Camera.Area的坐标系。 蓝色框显示相机区域的位置和形状,Rect值为333,333,667,667。

此坐标系的边界始终对应于相机预览中可见图像的外边缘,并且不会随缩放级别缩小或展开。 同样,使用Camera.setDisplayOrientation()旋转图像预览不会重新映射坐标系。

18、人脸检测

对于包含人物的照片,人脸通常是照片中最重要的部分,在拍摄图像时应该用于确定对焦和白平衡。 Android 4.0(API Level 14)框架提供了用于识别面部和使用面部识别技术计算图像设置的API。

注意:当面部检测功能正在运行时,setWhiteBalance(String),setFocusAreas(List <Camera.Area>)和setMeteringAreas(List <Camera.Area>)无效。

在相机应用程序中使用面部检测功能需要一些常规步骤:

检查设备是否支持面部检测
创建一个面部检测侦听器
将面部检测侦听器添加到相机对象
预览后(以及每次预览重启后)开始面部检测
并非所有设备都支持面部检测功能。您可以通过调用getMaxNumDetectedFaces()来检查是否支持此功能。下面的startFaceDetection()示例方法中显示了此检查的示例。

为了得到通知并响应面部检测,您的相机应用程序必须设置一个面部检测事件的监听器。为此,您必须创建一个实现Camera.FaceDetectionListener接口的侦听器类,如下面的示例代码所示。

class MyFaceDetectionListener implements Camera.FaceDetectionListener {

    @Override
    public void onFaceDetection(Face[] faces, Camera camera) {
        if (faces.length > 0){
            Log.d("FaceDetection", "face detected: "+ faces.length +
                    " Face 1 Location X: " + faces[0].rect.centerX() +
                    "Y: " + faces[0].rect.centerY() );
        }
    }
}

创建此类后,然后将其设置为应用程序的Camera对象,如下面的示例代码所示:

mCamera.setFaceDetectionListener(new MyFaceDetectionListener());

每次启动(或重新启动)摄像机预览时,您的应用程序都必须启动面部检测功能。 创建一个启动面部检测的方法,以便您可以根据需要调用它,如下面的示例代码所示。

public void startFaceDetection(){
    // Try starting Face Detection
    Camera.Parameters params = mCamera.getParameters();

    // start face detection only *after* preview has started
    if (params.getMaxNumDetectedFaces() > 0){
        // camera supports face detection, so can start it:
        mCamera.startFaceDetection();
    }
}

每次启动(或重新启动)相机预览时,都必须开始进行面部检测。 如果使用创建预览类中显示的预览类,请将startFaceDetection()方法添加到预览类中的surfaceCreated()和surfaceChanged()方法,如下面的示例代码所示。

public void surfaceCreated(SurfaceHolder holder) {
    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();

        startFaceDetection(); // start face detection feature

    } catch (IOException e) {
        Log.d(TAG, "Error setting camera preview: " + e.getMessage());
    }
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

    if (holder.getSurface() == null){
        // preview surface does not exist
        Log.d(TAG, "holder.getSurface() == null");
        return;
    }

    try {
        mCamera.stopPreview();

    } catch (Exception e){
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error stopping camera preview: " + e.getMessage());
    }

    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();

        startFaceDetection(); // re-start face detection feature

    } catch (Exception e){
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    }
}

注意:请记住在调用startPreview()之后调用此方法。 不要尝试在相机应用程序的主要活动的onCreate()方法中启动面部检测,因为在应用程序执行时此时无法进行预览。

19、时间流逝的视频
时间流逝视频允许用户创建视频剪辑,组合拍摄几秒或几分钟的照片。 此功能使用MediaRecorder记录时间间隔序列的图像。

要使用MediaRecorder录制定时视频,您必须配置录像机对象,就像录制普通视频一样,将每秒捕获的帧数设置为较低的数字并使用其中一个延时质量设置,如代码示例所示 下面。

// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH));
...
// Step 5.5: Set the video capture rate to a low number
mMediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds

必须在MediaRecorder的较大配置过程中完成这些设置。 有关完整配置代码示例,请参阅配置MediaRecorder。 配置完成后,您开始录制视频,就像录制普通视频片段一样。 有关配置和运行MediaRecorder的详细信息,请参阅捕获视频。

Android Camera2Video和Android HcrViewfinder示例进一步演示了本页面介绍的API的使用。

猜你喜欢

转载自www.cnblogs.com/adapter/p/9555977.html