【Camera】相机API(5)

Android框架支持设备上提供的各种相机和相机功能,使您可以在应用程序中捕捉照片和视频。本文档讨论了一种快速,简单的图像和视频捕获方法,并概述了为用户创建自定义相机体验的高级方法。

注意: 此页面描述了Camera已弃用的类。我们建议使用camera2适用于Android 5.0(API级别21)或更高版本的较新类。在我们的博客上了解更多关于camera2的信息, 并观看此视频。

请参阅以下相关资源:

注意事项


在让您的应用程序在Android设备上使用摄像头之前,您应该考虑一些关于您的应用程序打算如何使用此硬件功能的问题。

  • 摄像头要求 - 对于您的应用程序使用摄像头是否如此重要,以至于您不希望将应用程序安装在没有摄像头的设备上?如果是这样,您应该在清单中声明相机要求。
  • 快速照片或自定义相机 - 您的应用程序将如何使用相机?你只是想抓紧一个快速的图片或视频剪辑,或者你的应用程序提供了一种使用摄像机的新方法吗?要获得快速捕捉或剪辑,请考虑 使用现有的相机应用程序。要开发定制的相机功能,请查看构建相机应用程序部分。
  • 存储 - 您的应用程序生成的图像或视频是否仅用于应用程序或共享,以便其他应用程序(如图库或其他媒体和社交应用程序)可以使用它们?即使卸载应用程序,是否也希望图片和视频可用?查看Saving Media Files部分了解如何实现这些选项。

基础


Android框架支持通过android.hardware.camera2API或相机捕获图像和视频 Intent。这里是相关的类:

android.hardware.camera2
该软件包是控制设备摄像头的主要API。当您构建相机应用程序时,它可用于拍摄照片或视频。

Camera
该类是用于控制设备摄像头的较旧的弃用API。

SurfaceView
此课程用于向用户展示实时相机预览。

MediaRecorder
该课程用于记录来自摄像机的视频。

Intent
意图动作类型MediaStore.ACTION_IMAGE_CAPTURE或MediaStore.ACTION_VIDEO_CAPTURE可用于捕获图像或视频而不直接使用该Camera对象。

扫描二维码关注公众号,回复: 1511188 查看本文章

清单声明


在使用Camera API开始您的应用程序开发之前,您应该确保您的清单具有适当的声明以允许使用相机硬件和其他相关功能。

相机权限 - 您的应用程序必须请求使用设备相机的权限。

<uses-permission android:name="android.permission.CAMERA" />

注意:如果您通过调用现有的相机应用程序来使用相机,则您的应用程序无需请求此权限。

相机功能 - 您的应用程序还必须声明使用相机功能,例如:

<uses-feature android:name="android.hardware.camera" />

有关相机功能的列表,请参阅清单 功能参考

将相机功能添加到清单会导致Google Play阻止将您的应用程序安装到不包含相机或不支持您指定的相机功能的设备上。有关在Google Play中使用基于功能的过滤功能的更多信息,请参阅Google Play和基于功能的过滤

如果您的应用程序可以使用相机或相机功能进行正确的操作,但不需要它,则应通过包含该android:required属性并将其设置为false:

<uses-feature android:name="android.hardware.camera" android:required="false" />

存储权限 - 如果您的应用程序将图像或视频保存到设备的外部存储(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" />
...
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />

有关获取用户位置的更多信息,请参阅 位置策略

使用现有的相机应用


无需额外代码即可在应用程序中拍摄照片或视频的快速方法是使用一个Intent来调用现有的Android相机应用程序。详细信息在简单拍摄照片和 录制视频的培训课程中进行了介绍 。

构建相机应用程序


某些开发人员可能需要一个根据其应用程序外观进行定制的相机用户界面,或提供特殊功能。编写自己的照片拍摄代码可以为用户提供更具吸引力的体验。

注意:以下指南适用于较旧的,弃用的Camera API。对于新的或高级的相机应用,android.hardware.camera2建议使用较新的API。

为您的应用程序创建自定义相机界面的一般步骤如下:

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

相机硬件是必须小心管理的共享资源,以便您的应用程序不会与其他可能也想使用它的应用程序发生冲突。以下各节讨论如何检测相机硬件,如何请求访问相机,如何捕捉照片或视频以及如何在您的应用程序完成使用时释放相机。

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

检测相机硬件

如果您的应用程序没有特别要求使用清单声明的相机,您应该检查相机在运行时是否可用。要执行此检查,请使用该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 Level 9)和更高版本允许您使用该Camera.getNumberOfCameras()方法检查设备上可用的摄像头数量。

访问摄像头

如果您确定您的应用程序运行的设备具有摄像头,则必须通过获取实例Camera(除非您正在使用访问摄像头的意图)来请求访问它。

要访问主摄像头,请使用该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
}

小心:使用时务必检查异常情况Camera.open()。如果相机正在使用或不存在,则无法检查异常会导致您的应用程序被系统关闭。

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

检查相机功能

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

创建预览类

要使用户有效地拍摄照片或视频,他们必须能够看到设备摄像头看到的内容。相机预览课程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()方法中设置任意值。

注意: 随着Android 7.0(API级别24)及更高版本中的 多窗口功能的引入,即使在调用之后,您也不能再假定预览的宽高比与您的活动相同setDisplayOrientation()。根据窗口大小和宽高比,您可能必须使用信箱布局将宽屏相机预览放入纵向布局,反之亦然。

在布局中放置预览

相机预览课程(如上一节中所示的示例)必须放置在活动布局中,以及其他用户界面控件以拍摄照片或视频。本部分向您展示如何构建预览的基本布局和活动。

以下布局代码提供了可用于显示相机预览的非常基本的视图。在这个例子中,该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 Level 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);
    }
}

注意:所述的getCameraInstance()在上述示例方法指的是在所示的示例性方法访问摄像机。

捕捉图片

一旦你建立了一个预览类和一个视图布局来显示它,你就可以开始使用你的应用程序捕获图像。在您的应用程序代码中,您必须为您的用户界面控件设置侦听器,以便通过拍摄照片来响应用户操作。

为了检索图片,请使用该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: " +
                e.getMessage());
            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(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);
        }
    }
);

注意:mPicture以下示例中的成员引用上面的示例代码。

警告:请记住Camera 通过调用Camera.release()应用程序完成时使用它来释放对象!有关如何释放相机的信息,请参阅释放相机。

捕捉视频

使用Android框架进行视频捕捉需要仔细管理Camera对象并与MediaRecorder 课程协调。录制视频与当Camera,你必须管理Camera.lock()和Camera.unlock()调用允许MediaRecorder访问摄像头的硬件,除了Camera.open()和Camera.release()电话

:与Android 4.0(API级别14)开始,Camera.lock()并Camera.unlock()呼叫自动为您进行管理。

与使用设备相机拍摄照片不同,捕捉视频需要非常特定的呼叫次序。您必须按照特定的执行顺序才能成功准备和捕获视频,如下所述。

  1. 打开相机 - 使用Camera.open() 获取相机对象的实例。
  2. 连接预览 - 通过使用连接SurfaceView到相机准备实时相机图像预览Camera.setPreviewDisplay()。
  3. 开始预览 - 通话Camera.startPreview()开始显示实时相机图像。
  4. 开始录制视频 - 必须完成以下步骤才能成功录制视频:

    解锁相机 - MediaRecorder通过调用解锁相机以供使用Camera.unlock()。
    配置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 通过调用准备提供的配置设置MediaRecorder.prepare()。
    启动MediaRecorder - 通过调用开始录制视频MediaRecorder.start()。

停止录制视频 - 按顺序调用以下方法,以成功完成视频录制:
停止MediaRecorder - 通过呼叫停止录制视频MediaRecorder.stop()。
重置MediaRecorder - 或者,通过调用从记录器中删除配置设置MediaRecorder.reset()。
释放MediaRecorder - MediaRecorder 通过呼叫释放MediaRecorder.release()。
锁定相机 - 锁定相机,以便将来的MediaRecorder会话可以通过呼叫使用它Camera.lock()。从Android 4.0(API级别14)开始,除非MediaRecorder.prepare()呼叫失败,否则不需要此 呼叫。

停止预览 - 当您的活动完成使用相机时,请停止使用预览Camera.stopPreview()。

释放相机 - 释放相机,以便其他应用程序可以通过呼叫使用它Camera.release()。

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

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

配置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()

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

  1. 用相机解锁相机 Camera.unlock()
  2. MediaRecorder按照上面的代码示例进行配置
  3. 使用开始录制 MediaRecorder.start()
  4. 录制视频
  5. 停止使用记录 MediaRecorder.stop()
  6. 释放媒体记录器 MediaRecorder.release()
  7. 使用锁定相机 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实例。

释放相机

摄像头是设备上的应用程序共享的资源。您的应用程序可以在获取实例后使用相机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;
        }
    }
}

小心:如果您的应用程序未正确释放相机,则随后所有尝试访问相机(包括您自己的应用程序)的尝试都将失败,并可能导致您的应用程序或其他应用程序关闭。

保存媒体文件


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

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) - 此方法返回保存图片和视频的标准,共享和推荐位置。此目录是共享的(公共),所以其他应用程序可以轻松地发现,读取,更改和删除保存在此位置的文件。如果您的应用程序由用户卸载,则保存到此位置的媒体文件将不会被删除。为避免干扰用户现有的图片和视频,您应该在该目录中为应用程序的媒体文件创建一个子目录,如下面的代码示例所示。此方法在Android 2.2(API Level 8)中可用,对于早期API版本中的等效调用,请参阅保存共享文件。
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - 此方法返回一个标准位置,用于保存与您的应用程序关联的图片和视频。如果您的应用程序已卸载,则删除此位置中保存的任何文件。对此位置中的文件不执行安全性,其他应用程序可能会读取,更改和删除它们。
    以下示例代码演示了如何创建媒体文件File或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;
}

注意: Environment.getExternalStoragePublicDirectory()在Android 2.2(API级别8)或更高版本中可用。如果您使用早期版本的Android定位设备,请Environment.getExternalStorageDirectory() 改用它。有关更多信息,请参阅保存共享文件。

要使URI支持工作概要文件,请首先 将文件URI转换为内容URI。随后,内容URI增加 EXTRA_OUTPUT 的Intent。

有关在Android设备上保存文件的更多信息,请参阅数据存储。

相机功能


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

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

表1.按照引入它们的Android API级别排序的常见相机功能。
相机API
Android框架支持设备上提供的各种相机和相机功能,使您可以在应用程序中捕捉照片和视频。本文档讨论了一种快速,简单的图像和视频捕获方法,并概述了为用户创建自定义相机体验的高级方法。

注意: 此页面描述了Camera已弃用的类。我们建议使用camera2适用于Android 5.0(API级别21)或更高版本的较新类。在我们的博客上了解更多关于camera2的信息, 并观看此视频。

请参阅以下相关资源:

MediaPlayer概述
数据和文件存储概述
注意事项
在让您的应用程序在Android设备上使用摄像头之前,您应该考虑一些关于您的应用程序打算如何使用此硬件功能的问题。

摄像头要求 - 对于您的应用程序使用摄像头是否如此重要,以至于您不希望将应用程序安装在没有摄像头的设备上?如果是这样,您应该在清单中声明相机要求。
快速照片或自定义相机 - 您的应用程序将如何使用相机?你只是想抓紧一个快速的图片或视频剪辑,或者你的应用程序提供了一种使用摄像机的新方法吗?要获得快速捕捉或剪辑,请考虑 使用现有的相机应用程序。要开发定制的相机功能,请查看构建相机应用程序部分。
存储 - 您的应用程序生成的图像或视频是否仅用于应用程序或共享,以便其他应用程序(如图库或其他媒体和社交应用程序)可以使用它们?即使卸载应用程序,是否也希望图片和视频可用?查看Saving Media Files部分了解如何实现这些选项。
基础
Android框架支持通过android.hardware.camera2API或相机捕获图像和视频 Intent。这里是相关的类:

android.hardware.camera2
该软件包是控制设备摄像头的主要API。当您构建相机应用程序时,它可用于拍摄照片或视频。
Camera
该类是用于控制设备摄像头的较旧的弃用API。
SurfaceView
此课程用于向用户展示实时相机预览。
MediaRecorder
该课程用于记录来自摄像机的视频。
Intent
意图动作类型MediaStore.ACTION_IMAGE_CAPTURE或MediaStore.ACTION_VIDEO_CAPTURE可用于捕获图像或视频而不直接使用该Camera对象。
清单声明
在使用Camera API开始您的应用程序开发之前,您应该确保您的清单具有适当的声明以允许使用相机硬件和其他相关功能。

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

相机功能 - 您的应用程序还必须声明使用相机功能,例如:
<uses-feature android:name = “android.hardware.camera” />
有关相机功能的列表,请参阅清单 功能参考。

将相机功能添加到清单会导致Google Play阻止将您的应用程序安装到不包含相机或不支持您指定的相机功能的设备上。有关在Google Play中使用基于功能的过滤功能的更多信息,请参阅Google Play和基于功能的过滤。

如果您的应用程序可以使用相机或相机功能进行正确的操作,但不需要它,则应通过包含该android:required属性并将其设置为false:

<uses-feature android:name = “android.hardware.camera” android:required = “false” />
存储权限 - 如果您的应用程序将图像或视频保存到设备的外部存储(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” />
...
<! - 仅当您的应用面向Android 5.0(API级别21)或更高时才需要。- > <uses-feature android:name = “android.hardware.location.gps” />

有关获取用户位置的更多信息,请参阅 位置策略。

使用现有的相机应用
无需额外代码即可在应用程序中拍摄照片或视频的快速方法是使用一个Intent来调用现有的Android相机应用程序。详细信息在简单拍摄照片和 录制视频的培训课程中进行了介绍 。

构建相机应用程序
某些开发人员可能需要一个根据其应用程序外观进行定制的相机用户界面,或提供特殊功能。编写自己的照片拍摄代码可以为用户提供更具吸引力的体验。

注意:以下指南适用于较旧的,弃用的Camera API。对于新的或高级的相机应用,android.hardware.camera2建议使用较新的API。

为您的应用程序创建自定义相机界面的一般步骤如下:

检测并访问摄像头 - 创建代码以检查摄像头是否存在并请求访问。
创建一个预览类 - 创建一个扩展SurfaceView和实现SurfaceHolder接口的相机预览类。此课程预览摄像机的实时图像。
构建预览版面 - 获得相机预览课程后,创建一个包含预览和用户界面控件的视图布局。
捕获设置侦听器 - 连接用户界面控件的侦听器,以响应用户操作(例如按下按钮)来启动图像或视频捕获。
捕获和保存文件 - 设置捕获图片或视频的代码并保存输出。
释放相机 - 使用相机后,您的应用程序必须正确释放以供其他应用程序使用。
相机硬件是必须小心管理的共享资源,以便您的应用程序不会与其他可能也想使用它的应用程序发生冲突。以下各节讨论如何检测相机硬件,如何请求访问相机,如何捕捉照片或视频以及如何在您的应用程序完成使用时释放相机。

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

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

/ *检查该设备带有相机 / 私人布尔checkCameraHardware (上下文语境){ 如果(背景。getPackageManager()。hasSystemFeature(PackageManager。FEATURE_CAMERA)){ //该设备带有相机 返回真; } else { //此设备上没有摄像头 返回false ; } }

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

访问摄像头
如果您确定您的应用程序运行的设备具有摄像头,则必须通过获取实例Camera(除非您正在使用访问摄像头的意图)来请求访问它。

要访问主摄像头,请使用该Camera.open()方法,并确保捕捉到任何异常情况,如下面的代码所示:

/ *获取Camera对象实例的安全方法。 / public static Camera getCameraInstance (){ Camera c = null ; 尝试{
c = 相机。open (); //尝试获取Camera实例 } catch (Exception e){ // Camera not available(in use or does not exist) } return c ; 如果相机不可用,则返回null }

小心:使用时务必检查异常情况Camera.open()。如果相机正在使用或不存在,则无法检查异常会导致您的应用程序被系统关闭。

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

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

创建预览类
要使用户有效地拍摄照片或视频,他们必须能够看到设备摄像头看到的内容。相机预览课程SurfaceView可以显示来自相机的实时图像数据,因此用户可以构建并捕获图片或视频。

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

/ *一个基本的Camera预览类 / 公共类CameraPreview 扩展了SurfaceView实现的SurfaceHolder。回调 { private SurfaceHolder mHolder ; 私人相机mCamera ;

公共CameraPreview (上下文上下文,摄像头摄像头){ 超级(上下文); 
    mCamera = 相机;    

    //安装SurfaceHolder.Callback,以便在底层表面被创建和销毁时得到通知  。 mHolder = getHolder(); mHolder。addCallback(

    这); //弃用设置,但在3.0 mHolder之前的Android版本上需要  。的setType(SurfaceHolder。SURFACE_TYPE_PUSH_BUFFERS); }

public void surfaceCreated (SurfaceHolder holder){ // Surface已经创建,现在告诉相机在哪里绘制预览。 尝试{  
        mCamera 。setPreviewDisplay (holder); 
        mCamera 。startPreview (); } 捕捉(IOException异常Ê){ 登录。d(TAG ,“错误设置相机预览:” + ê。的getMessage()); } }   

public void surfaceDestroyed (SurfaceHolder holder){ //空。照顾在您的活动中释放相机预览。 }   

public void surfaceChanged (SurfaceHolder holder,int format,int w,int h){ //如果您的预览可以更改或旋转,请在这里处理这些事件。 //确保在调整大小或重新格式化之前停止预览。          

    如果(mHolder。getSurface()== 空){ //预览表面不存在  返回; }   

    //在更改之前停止预览  尝试{  
        mCamera 。stopPreview (); } catch (Exception e){ //忽略:试图阻止不存在的预览  }

    //设置预览大小并在此处进行任何调整大小,旋转或  //重新格式化更改 

    //使用新设置开始预览  尝试{  
        mCamera 。setPreviewDisplay (mHolder); 
        mCamera 。startPreview ();

    } catch (Exception e){ Log。d(TAG ,“错误开始相机预览:” + ê。的getMessage()); } } }   

如果您想为相机预览设置特定尺寸,请surfaceChanged()按照上述注释中的说明在方法中进行设置。设置预览大小时,您 必须使用来自的值getSupportedPreviewSizes()。 不要在setPreviewSize()方法中设置任意值。

注意: 随着Android 7.0(API级别24)及更高版本中的 多窗口功能的引入,即使在调用之后,您也不能再假定预览的宽高比与您的活动相同setDisplayOrientation()。根据窗口大小和宽高比,您可能必须使用信箱布局将宽屏相机预览放入纵向布局,反之亦然。

在布局中放置预览
相机预览课程(如上一节中所示的示例)必须放置在活动布局中,以及其他用户界面控件以拍摄照片或视频。本部分向您展示如何构建预览的基本布局和活动。

以下布局代码提供了可用于显示相机预览的非常基本的视图。在这个例子中,该FrameLayout元素意味着是相机预览类的容器。使用此布局类型,以便可以在实时相机预览图像上叠加其他图片信息或控件。

<?xml version = “1.0” encoding = “utf-8” ?> <LinearLayout xmlns:android = “http://schemas.android.com/apk/res/android” android:orientation = “ horizo​​ntal ” android:layout_width = “ FILL_PARENT “ 机器人:layout_height = ” FILL_PARENT “ > <的FrameLayout 机器人:ID = ”@ + ID / camera_preview“ 机器人:layout_width = ” FILL_PARENT “ 机器人:

“ android:layout_weight = ”1“ />

<按钮机器人:ID = “@ + ID / button_capture” 机器人:文本 = “捕捉” 机器人:layout_width = “ WRAP_CONTENT ” 机器人:layout_height = “ WRAP_CONTENT ” 机器人:layout_gravity = “ 中心 ” /> </的LinearLayout>

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

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

      android:screenOrientation = “ landscape ” > <! - 将此活动配置为使用横向方向 - >

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

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

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

公共类CameraActivity 扩展活动 {

私人相机mCamera ; 私人CameraPreview mPreview ;  

@Override public void onCreate (Bundle savedInstanceState){ super 。onCreate (savedInstanceState); 的setContentView(ř。布局。主); 

    //创建Camera的一个实例  
    mCamera = getCameraInstance (); 

    //创建我们的预览视图并将其设置为我们活动的内容。 mPreview = 新的CameraPreview (
       这,mCamera ); 的FrameLayout 预览 = (的FrameLayout)findViewById(ř。ID。camera_preview ); 预览。addView(mPreview); } }

注意:所述的getCameraInstance()在上述示例方法指的是在所示的示例性方法访问摄像机。

捕捉图片
一旦你建立了一个预览类和一个视图布局来显示它,你就可以开始使用你的应用程序捕获图像。在您的应用程序代码中,您必须为您的用户界面控件设置侦听器,以便通过拍摄照片来响应用户操作。

为了检索图片,请使用该Camera.takePicture()方法。这种方法需要三个参数来接收摄像机的数据。为了接收JPEG格式的数据,您必须实现一个Camera.PictureCallback接口来接收图像数据并将其写入文件。以下代码显示了Camera.PictureCallback保存从相机接收的图像的界面的基本实现。

私人PictureCallback mPicture = 新PictureCallback (){

@覆盖公共空隙onPictureTaken (字节[] 数据,摄像机相机){ 

    File pictureFile = getOutputMediaFile (MEDIA_TYPE_IMAGE ); 如果(pictureFile == null ){ Log。d(TAG ,“错误创建媒体文件,检查存储权限:” + 
            ê 。的getMessage ()); 返回; }  

    尝试{ FileOutputStream fos = 新的FileOutputStream(pictureFile); fos。写(数据); fos。close(); } catch (FileNotFoundException e){ Log。d(TAG ,“未找到文件:” + ê。的getMessage()); } 捕捉(IOException异常Ê){ 登录。d(TAG  

        ,“访问文件时出错:” + e。getMessage()); } } };   

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

//收听者添加到捕捉按钮 按钮captureButton = (按钮)findViewById(ID 。button_capture );
captureButton 。setOnClickListener (新景观。OnClickListener (){ @覆盖公共无效的onClick (视图v){ //从相机图像
mCamera 。takePicture (空,空,mPicture ); }

} ); 

注意:mPicture以下示例中的成员引用上面的示例代码。

警告:请记住Camera 通过调用Camera.release()应用程序完成时使用它来释放对象!有关如何释放相机的信息,请参阅释放相机。

捕捉视频
使用Android框架进行视频捕捉需要仔细管理Camera对象并与MediaRecorder 课程协调。录制视频与当Camera,你必须管理Camera.lock()和Camera.unlock()调用允许MediaRecorder访问摄像头的硬件,除了Camera.open()和Camera.release()电话。

注:与Android 4.0(API级别14)开始,Camera.lock()并Camera.unlock()呼叫自动为您进行管理。

与使用设备相机拍摄照片不同,捕捉视频需要非常特定的呼叫次序。您必须按照特定的执行顺序才能成功准备和捕获视频,如下所述。

打开相机 - 使用Camera.open() 获取相机对象的实例。
连接预览 - 通过使用连接SurfaceView到相机准备实时相机图像预览Camera.setPreviewDisplay()。
开始预览 - 通话Camera.startPreview()开始显示实时相机图像。
开始录制视频 - 必须完成以下步骤才能成功录制视频:
解锁相机 - MediaRecorder通过调用解锁相机以供使用Camera.unlock()。
配置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 通过调用准备提供的配置设置MediaRecorder.prepare()。
启动MediaRecorder - 通过调用开始录制视频MediaRecorder.start()。
停止录制视频 - 按顺序调用以下方法,以成功完成视频录制:
停止MediaRecorder - 通过呼叫停止录制视频MediaRecorder.stop()。
重置MediaRecorder - 或者,通过调用从记录器中删除配置设置MediaRecorder.reset()。
释放MediaRecorder - MediaRecorder 通过呼叫释放MediaRecorder.release()。
锁定相机 - 锁定相机,以便将来的MediaRecorder会话可以通过呼叫使用它Camera.lock()。从Android 4.0(API级别14)开始,除非MediaRecorder.prepare()呼叫失败,否则不需要此 呼叫。
停止预览 - 当您的活动完成使用相机时,请停止使用预览Camera.stopPreview()。
释放相机 - 释放相机,以便其他应用程序可以通过呼叫使用它Camera.release()。
注意:可以在MediaRecorder 不创建相机预览的情况下使用,并跳过此过程的前几个步骤。但是,由于用户通常喜欢在开始录制之前看到预览,因此此处不讨论该过程。

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

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

private boolean prepareVideoRecorder (){

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

//步骤1:解锁并将相机设置为MediaRecorder  
mCamera 。解锁();  
mMediaRecorder 。setCamera (mCamera ); 

//第2步:设置来源  
mMediaRecorder 。setAudioSource (MediaRecorder。的AudioSource。CAMCORDER); 
mMediaRecorder 。setVideoSource (MediaRecorder。VideoSource。CAMERA);

//步骤3:设置CamcorderProfile(需要API等级8或更高)  
mMediaRecorder 。setProfile (CamcorderProfile。得到(CamcorderProfile。QUALITY_HIGH));

//第4步:设置输出文件  
mMediaRecorder 。setOutputFile (getOutputMediaFile (MEDIA_TYPE_VIDEO 。)的toString ()); 

//第5步:设置预览输出  
mMediaRecorder 。setPreviewDisplay (mPreview 。getHolder ()。getSurface ()); 

//第6步:准备配置的MediaRecorder  尝试{  
    mMediaRecorder 。准备(); } catch (IllegalStateException e){ Log。d(TAG ,“IllegalStateException异常制备MediaRecorder:” + ê。的getMessage()); 
    releaseMediaRecorder (); 返回false ; } 捕捉(IOException异常Ê){ 登录。d(TAG ,

     “IOException准备MediaRecorder:” + e。getMessage()); 
    releaseMediaRecorder (); 返回false ; } 返回true ; }   

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

//步骤3:设置输出格式和编码(针对API Level 8之前的版本)  
mMediaRecorder 。setOutputFormat (MediaRecorder。OUTPUTFORMAT。MPEG_4); 
mMediaRecorder 。setAudioEncoder (MediaRecorder。AudioEncoder。DEFAULT); 
mMediaRecorder 。setVideoEncoder (MediaRecorder。视频编码。DEFAULT);

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

setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
启动和停止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(); 

                //告知用户记录已经开始  
                setCaptureButtonText (“Stop” ); isRecording = true ; } 其他{/ / 准备没有工作,释放相机  
                releaseMediaRecorder (); //通知用户  } } } } ); 

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

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

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

公共类CameraActivity 扩展活动 { 私人相机mCamera ; 私人SurfaceView mPreview ; 私人MediaRecorder mMediaRecorder ;

... 

@Override 保护无效onPause (){ 超级。onPause ();  
    releaseMediaRecorder (); //如果你正在使用MediaRecorder,松开第一  
    releaseCamera (); //暂停时立即释放相机  } 

private void releaseMediaRecorder (){ if (mMediaRecorder!= null ){ mMediaRecorder。reset(); //清除记录器配置mMediaRecorder。release(); //释放记录器对象mMediaRecorder = null ; 
        mCamera 。lock (); //锁定相机供以后使用  } }  

private void releaseCamera (){ if (mCamera != null ){  
        mCamera 。release (); //为其他应用程序释放相机  
        mCamera = null ; } } }  

小心:如果您的应用程序未正确释放相机,则随后所有尝试访问相机(包括您自己的应用程序)的尝试都将失败,并可能导致您的应用程序或其他应用程序关闭。

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

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

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

/ *创建一个文件用于保存图像或视频的Uri / private static Uri getOutputMediaFileUri (int type){ return Uri。fromFile(getOutputMediaFile (type)); }

/ *创建一个文件保存图像或视频 / 私人静态文件 getOutputMediaFile (INT 类型){ //为了安全起见,你应该检查SD卡挂载/ / 使用Environment.getExternalStorageState()之前这样做。

文件mediaStorageDir = 新文件(环境。getExternalStoragePublicDirectory(环境。DIRECTORY_PICTURES),“MyCameraApp” ); //如果您希望在应用程序之间共享已创建的图像,并且在应用程序卸载后仍然存在,则此位置效果最佳  。     

//创建存储目录,如果它不存在,  如果(!mediaStorageDir。存在()){ 如果(!mediaStorageDir。mkdirs()){ 登录。d(“MyCameraApp” ,“无法创建目录” ); 返回null ; } }

//创建一个媒体文件名  字符串timeStamp = new SimpleDateFormat(“yyyyMMdd_HHmmss” )。格式(新日期()); 文件mediaFile ; 如果(类型 == MEDIA_TYPE_IMAGE){ 媒体文件 = 新文件(mediaStorageDir。的getPath()+ 文件。隔板 + “IMG_” + 的timeStamp + “.JPG” ); } else if (

  类型 == MEDIA_TYPE_VIDEO){ 媒体文件 = 新文件(mediaStorageDir。的getPath()+ 文件。隔板 + “VID_” + 的timeStamp + ”的.mp4" ); } else { return null ; }   

返回mediaFile ; } 

注意: Environment.getExternalStoragePublicDirectory()在Android 2.2(API级别8)或更高版本中可用。如果您使用早期版本的Android定位设备,请Environment.getExternalStorageDirectory() 改用它。有关更多信息,请参阅保存共享文件。

要使URI支持工作概要文件,请首先 将文件URI转换为内容URI。随后,内容URI增加 EXTRA_OUTPUT 的Intent。

有关在Android设备上保存文件的更多信息,请参阅数据存储。

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

计量和重点领域
人脸检测
时间推移视频
有关如何使用通过控制的功能的一般信息Camera.Parameters,请查看使用相机功能部分。有关如何使用通过相机参数对象控制的功能的更多详细信息,请按照以下功能列表中的链接参阅API参考文档。

表1.按照引入它们的Android API级别排序的常见相机功能。
特征 API级别 描述
人脸检测 14 识别照片中的人脸并将其用于对焦,测光和白平衡
测量区域 14 指定图像中的一个或多个区域以计算白平衡
重点领域 14 设置图像中的一个或多个区域以用于焦点
White Balance Lock 14 停止或开始自动白平衡调整
Exposure Lock 14 停止或开始自动曝光调整
Video Snapshot 14 拍摄视频时拍摄照片(抓图)
时间推移录影 11 记录具有设置延迟的帧以记录延时视频
Multiple Cameras 9 支持设备上的多个摄像头,包括前置摄像头和后置摄像头
Focus Distance 9 报告相机与看起来焦点对象之间的距离
Zoom 8 设置图像放大
Exposure Compensation 8 增加或减少曝光量
GPS Data 五 在图像中包含或省略地理位置数据
White Balance 五 设置白平衡模式,该模式会影响拍摄图像中的颜色值
Focus Mode 五 设置相机如何对焦于诸如自动,固定,宏观或无限等主题
Scene Mode 五 针对特定类型的拍摄场景(如夜景,沙滩,雪景或烛光场景)应用预设模式
JPEG Quality 五 设置JPEG图像的压缩级别,从而增加或减少图像输出文件的质量和大小
Flash Mode 五 打开闪光灯,关闭或使用自动设置
Color Effects 五 对拍摄的图像应用颜色效果,如黑白,棕褐色调或负片。
Anti-Banding 五 由于JPEG压缩,减少了颜色渐变中的条带效应
Picture Format 1 指定图片的文件格式
Picture Size 1 指定保存的图片的像素尺寸

注意:由于硬件差异和软件实施,所有设备都不支持这些功能。有关检查运行应用程序的设备上功能的可用性的信息,请参阅检查功能可用性。

检查功能可用性

在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
}

您可以使用上面显示的大多数相机功能的技巧。所述 Camera.Parameters对象提供一个getSupported...(),is...Supported()或getMax...()方法,以确定是否(以及在何种程度)的特征的支持。

如果您的应用程序需要某些相机功能才能正常运行,则可以通过添加应用程序清单来要求它们。当您声明使用特定的相机功能(如闪光灯和自动对焦)时,Google Play会限制您的应用程序安装在不支持这些功能的设备上。有关可在应用程序清单中声明的​​相机功能列表,请参阅清单 功能参考。

使用相机功能

大多数相机功能都是使用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 Level 14)开始,可以更改预览方向而无需重新开始预览。

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

  • 计量和重点领域
  • 人脸检测
  • 时间推移视频
    以下部分提供了如何实现这些功能的简要概述。

计量和重点领域

在某些摄影场景中,自动对焦和测光可能无法产生理想的效果。从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用于指定摄像机视场内的区域的对象和一个权重值,该值指示摄像机在测光或焦点计算中应给出该区域的重要程度。

对象中的Rect字段Camera.Area描述了映射到2000 x 2000单位网格上的矩形形状。坐标-1000,-1000代表摄像机图像的左上角,坐标1000,1000代表摄像机图像的右下角,如下图所示。
【Camera】相机API(5)
图1.红线表示用于Camera.Area在相机预览中指定的坐标系 。蓝色框显示Rect值为333,333,667,667 的摄像机区域的位置和形状。

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

人脸检测

对于包含人物的照片,人脸通常是照片中最重要的部分,应该在拍摄图像时用于确定对焦和白平衡。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 (mHolder.getSurface() == null){
        // preview surface does not exist
        Log.d(TAG, "mHolder.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(mHolder);
        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()相机应用程序的主要活动的方法中启动人脸检测,因为在应用程序执行过程中,此预览不可用。

时间推移视频

时间推移视频允许用户创建视频剪辑,将几秒钟或几分钟之间拍摄的照片合并在一起。此功能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。

其他示例代码


要下载有关Camera2 basic的示例应用程序,请参阅 Android Camera2Basic Sample。要下载关于Camera2 raw的示例应用程序,请参阅 Android Camera2Raw Sample

Lastest Update:2018.04.17

联系我

QQ:94297366
微信打赏:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ

公众号推荐:

【Camera】相机API(5)

猜你喜欢

转载自blog.51cto.com/4789781/2125838