Android 框架包含对设备上各种相机和相机功能的支持,使您能够在应用中捕获图片和视频。本文档讨论了一种快速简单的图像和视频捕获方法,并概述了一种为用户创建自定义相机体验的高级方法。
注意:此页面描述了已弃用的Camera
类。我们建议您使用 CameraX Jetpack 库,或者对于特定用例,使用 camera2
类。CameraX 和 Camera2 都适用于 Android 5.0(API 级别 21)及更高版本。
请参阅以下相关资源
注意事项
在启用您的应用程序以在 Android 设备上使用摄像头之前,您应该考虑一些关于您的应用程序打算如何使用此硬件功能的问题。
- 摄像头要求 - 摄像头对于您的应用程序是否如此重要,以至于您不希望在没有摄像头的设备上安装您的应用程序?如果是这样,您应该在 清单中声明摄像头要求。
- 快速拍照或自定义摄像头 - 您的应用程序将如何使用摄像头?您是否只对拍摄快速照片或视频剪辑感兴趣,或者您的应用程序是否会提供一种新的使用摄像头的方式?对于快速拍摄照片或剪辑,请考虑 使用现有的摄像头应用程序。对于开发自定义摄像头功能,请查看 构建摄像头应用程序 部分。
- 前台服务要求 - 您的应用程序何时与摄像头交互?在 Android 9(API 级别 28)及更高版本中,在后台运行的应用程序无法访问摄像头。因此,您应该在应用程序处于前台或作为 前台服务 的一部分时使用摄像头。
- 存储 - 您的应用程序生成的图像或视频是否仅供您的应用程序查看,还是共享以便其他应用程序(如图库或其他媒体和社交应用程序)可以使用?即使您的应用程序被卸载,您是否希望图片和视频可用?查看 保存媒体文件 部分以了解如何实现这些选项。
基础知识
Android 框架通过 android.hardware.camera2
API 或摄像头 Intent
支持捕获图像和视频。以下是相关的类
android.hardware.camera2
- 此包是控制设备摄像头的主要 API。当您构建摄像头应用程序时,它可以用于拍摄照片或视频。
Camera
- 此类是用于控制设备摄像头的较旧的已弃用 API。
SurfaceView
- 此类用于向用户呈现实时摄像头预览。
MediaRecorder
- 此类用于录制来自摄像头的视频。
Intent
MediaStore.ACTION_IMAGE_CAPTURE
或MediaStore.ACTION_VIDEO_CAPTURE
的 Intent 操作类型可用于捕获图像或视频,而无需直接使用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" />
- 存储权限 - 如果您的应用程序的目标是 Android 10(API 级别 29)或更低版本并在清单中指定以下内容,则它可以将图像或视频保存到设备的外部存储(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.release()
来释放 Camera
对象!如果您的应用程序没有正确释放摄像头,则所有后续尝试访问摄像头的操作(包括您自己的应用程序)都将失败,并可能导致您的应用程序或其他应用程序被关闭。
检测摄像头硬件
如果您的应用程序没有使用清单声明明确要求摄像头,则您应该检查运行时是否可用。要执行此检查,请使用 PackageManager.hasSystemFeature()
方法,如下面的示例代码所示
Kotlin
/** Check if this device has a camera */ private fun checkCameraHardware(context: Context): Boolean { if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)) { // this device has a camera return true } else { // no camera on this device return false } }
Java
/** 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()
方法检查设备上可用的摄像头数量。
访问摄像头
如果您已确定您的应用程序正在运行的设备具有摄像头,则必须通过获取 Camera
的实例来请求访问它(除非您正在使用 意图访问摄像头)。
要访问主摄像头,请使用 Camera.open()
方法,并确保捕获任何异常,如下面的代码所示
Kotlin
/** A safe way to get an instance of the Camera object. */ fun getCameraInstance(): Camera? { return try { Camera.open() // attempt to get a Camera instance } catch (e: Exception) { // Camera is not available (in use or does not exist) null // returns null if camera is unavailable } }
Java
/** 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 级别 9)或更高版本的设备上,您可以使用 Camera.open(int)
访问特定的摄像头。上面的示例代码将访问具有多个摄像头的设备上的第一个后置摄像头。
检查摄像头功能
获得对摄像头的访问权限后,您可以使用 Camera.getParameters()
方法并检查返回的 Camera.Parameters
对象以获取支持的功能,从而获取有关其功能的更多信息。当使用 API 级别 9 或更高版本时,请使用 Camera.getCameraInfo()
来确定摄像头是在设备的前面还是后面,以及图像的方向。
创建预览类
为了让用户有效地拍摄照片或视频,他们必须能够看到设备摄像头看到的内容。摄像头预览类是一个 SurfaceView
,可以显示来自摄像头的实时图像数据,以便用户可以构图和捕获图片或视频。
以下示例代码演示了如何创建一个基本摄像头预览类,该类可以包含在 View
布局中。此类实现 SurfaceHolder.Callback
以捕获创建和销毁视图的回调事件,这些事件是分配摄像头预览输入所必需的。
Kotlin
/** A basic Camera preview class */ class CameraPreview( context: Context, private val mCamera: Camera ) : SurfaceView(context), SurfaceHolder.Callback { private val mHolder: SurfaceHolder = holder.apply { // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. addCallback(this@CameraPreview) // deprecated setting, but required on Android versions prior to 3.0 setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } override fun surfaceCreated(holder: SurfaceHolder) { // The Surface has been created, now tell the camera where to draw the preview. mCamera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: IOException) { Log.d(TAG, "Error setting camera preview: ${e.message}") } } } override fun surfaceDestroyed(holder: SurfaceHolder) { // empty. Take care of releasing the Camera preview in your activity. } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { // 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.surface == null) { // preview surface does not exist return } // stop preview before making changes try { mCamera.stopPreview() } catch (e: Exception) { // 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 mCamera.apply { try { setPreviewDisplay(mHolder) startPreview() } catch (e: Exception) { Log.d(TAG, "Error starting camera preview: ${e.message}") } } } }
Java
/** 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()
,也不能再假设预览的纵横比与您的 Activity 相同。根据窗口大小和纵横比,您可能需要使用 letterbox 布局将宽的相机预览适配到纵向布局,反之亦然。
在布局中放置预览
相机预览类(例如上一节中显示的示例)必须与其他用于拍照或录制的用户界面控件一起放置在 Activity 的布局中。本节将向您展示如何构建用于预览的基本布局和 Activity。
以下布局代码提供了一个非常基本的视图,可用于显示相机预览。在此示例中,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 方向更改为横向,方法是在清单中添加以下内容。
<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()
重新开始预览。
在相机视图的 Activity 中,将您的预览类添加到上面示例中所示的 FrameLayout
元素中。您的相机 Activity 还必须确保在暂停或关闭时释放相机。以下示例展示了如何修改相机 Activity 以附加 创建预览类 中显示的预览类。
Kotlin
class CameraActivity : Activity() { private var mCamera: Camera? = null private var mPreview: CameraPreview? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Create an instance of Camera mCamera = getCameraInstance() mPreview = mCamera?.let { // Create our Preview view CameraPreview(this, it) } // Set the Preview view as the content of our activity. mPreview?.also { val preview: FrameLayout = findViewById(R.id.camera_preview) preview.addView(it) } } }
Java
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
接口的基本实现,用于保存从相机接收到的图像。
Kotlin
private val mPicture = Camera.PictureCallback { data, _ -> val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE) ?: run { Log.d(TAG, ("Error creating media file, check storage permissions")) return@PictureCallback } try { val fos = FileOutputStream(pictureFile) fos.write(data) fos.close() } catch (e: FileNotFoundException) { Log.d(TAG, "File not found: ${e.message}") } catch (e: IOException) { Log.d(TAG, "Error accessing file: ${e.message}") } }
Java
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
中调用此方法。
Kotlin
val captureButton: Button = findViewById(R.id.button_capture) captureButton.setOnClickListener { // get an image from the camera mCamera?.takePicture(null, null, picture) }
Java
// 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, picture); } } );
注意: 以下示例中的 mPicture
成员指的是上面的示例代码。
注意: 请记住,在应用程序完成使用 Camera
对象后,通过调用 Camera.release()
释放它!有关如何释放相机的更多信息,请参阅 释放相机。
捕获视频
使用 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 级别 8)及更高版本,使用
MediaRecorder.setProfile
方法,并使用CamcorderProfile.get()
获取配置文件实例。对于 Android 2.2 之前的版本,您必须设置视频输出格式和编码参数setOutputFormat()
- 设置输出格式,指定默认设置或MediaRecorder.OutputFormat.MPEG_4
。setAudioEncoder()
- 设置声音编码类型,指定默认设置或MediaRecorder.AudioEncoder.AMR_NB
。setVideoEncoder()
- 设置视频编码类型,指定默认设置或MediaRecorder.VideoEncoder.MPEG_4_SP
。
setOutputFile()
- 设置输出文件,使用 保存媒体文件 部分示例方法中的getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()
。setPreviewDisplay()
- 为您的应用程序指定SurfaceView
预览布局元素。使用与连接预览中指定的相同对象。
注意: 必须按此顺序调用这些
MediaRecorder
配置方法,否则您的应用程序将遇到错误,并且录制将失败。 - 准备 MediaRecorder - 通过调用
MediaRecorder.prepare()
使用提供的配置设置准备MediaRecorder
。 - 启动 MediaRecorder - 通过调用
MediaRecorder.start()
开始录制视频。
- 解锁相机 - 通过调用
- 停止录制视频 - 按顺序调用以下方法,以成功完成视频录制
- 停止 MediaRecorder - 通过调用
MediaRecorder.stop()
停止录制视频。 - 重置 MediaRecorder - 可选地,通过调用
MediaRecorder.reset()
从录制器中删除配置设置。 - 释放 MediaRecorder - 通过调用
MediaRecorder.release()
释放MediaRecorder
。 - 锁定相机 - 锁定相机,以便将来
MediaRecorder
会话可以使用它,方法是调用Camera.lock()
。从 Android 4.0(API 级别 14)开始,除非MediaRecorder.prepare()
调用失败,否则不需要此调用。
- 停止 MediaRecorder - 通过调用
- 停止预览 - 当您的 Activity 完成使用相机后,使用
Camera.stopPreview()
停止预览。 - 释放相机 - 通过调用
Camera.release()
释放相机,以便其他应用程序可以使用它。
注意: 可以先不创建相机预览,然后跳过此过程的前几个步骤,来使用 MediaRecorder
。但是,由于用户通常希望在开始录制之前看到预览,因此此处不讨论该过程。
提示:如果您的应用程序通常用于录制视频,请在开始预览之前将setRecordingHint(boolean)
设置为true
。此设置可以帮助缩短开始录制所需的时间。
配置 MediaRecorder
当使用MediaRecorder
类录制视频时,必须按照特定顺序执行配置步骤,然后调用MediaRecorder.prepare()
方法来检查和实现配置。以下示例代码演示了如何正确配置和准备MediaRecorder
类以进行视频录制。
Kotlin
private fun prepareVideoRecorder(): Boolean { mediaRecorder = MediaRecorder() mCamera?.let { camera -> // Step 1: Unlock and set camera to MediaRecorder camera?.unlock() mediaRecorder?.run { setCamera(camera) // Step 2: Set sources setAudioSource(MediaRecorder.AudioSource.CAMCORDER) setVideoSource(MediaRecorder.VideoSource.CAMERA) // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)) // Step 4: Set output file setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()) // Step 5: Set the preview output setPreviewDisplay(mPreview?.holder?.surface) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) // Step 6: Prepare configured MediaRecorder return try { prepare() true } catch (e: IllegalStateException) { Log.d(TAG, "IllegalStateException preparing MediaRecorder: ${e.message}") releaseMediaRecorder() false } catch (e: IOException) { Log.d(TAG, "IOException preparing MediaRecorder: ${e.message}") releaseMediaRecorder() false } } } return false }
Java
private boolean prepareVideoRecorder(){ mCamera = getCameraInstance(); mediaRecorder = new MediaRecorder(); // Step 1: Unlock and set camera to MediaRecorder mCamera.unlock(); mediaRecorder.setCamera(mCamera); // Step 2: Set sources mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); // Step 4: Set output file mediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()); // Step 5: Set the preview output mediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface()); // Step 6: Prepare configured MediaRecorder try { mediaRecorder.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 级别 8)之前,必须直接设置输出格式和编码格式参数,而不是使用CamcorderProfile
。以下代码演示了这种方法。
Kotlin
// Step 3: Set output format and encoding (for versions prior to API Level 8) mediaRecorder?.apply { setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) }
Java
// Step 3: Set output format and encoding (for versions prior to API Level 8) mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
以下MediaRecorder
的视频录制参数具有默认设置,但是,您可能希望根据您的应用程序调整这些设置。
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
启动和停止 MediaRecorder
在使用MediaRecorder
类启动和停止视频录制时,必须按照以下列出的特定顺序进行操作。
- 使用
Camera.unlock()
解锁相机。 - 如上文代码示例所示配置
MediaRecorder
。 - 使用
MediaRecorder.start()
开始录制。 - 录制视频。
- 使用
MediaRecorder.stop()
停止录制。 - 使用
MediaRecorder.release()
释放媒体录制器。 - 使用
Camera.lock()
锁定相机。
以下示例代码演示了如何将按钮连接起来,以使用相机和MediaRecorder
类正确启动和停止视频录制。
注意:完成视频录制后,请勿释放相机,否则您的预览将停止。
Kotlin
var isRecording = false val captureButton: Button = findViewById(R.id.button_capture) captureButton.setOnClickListener { if (isRecording) { // stop recording and release camera mediaRecorder?.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 mediaRecorder?.start() // inform the user that recording has started setCaptureButtonText("Stop") isRecording = true } else { // prepare didn't work, release the camera releaseMediaRecorder() // inform user } } }
Java
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 mediaRecorder.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 mediaRecorder.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()
方法,如下面的示例代码所示。
Kotlin
class CameraActivity : Activity() { private var mCamera: Camera? private var preview: SurfaceView? private var mediaRecorder: MediaRecorder? override fun onPause() { super.onPause() releaseMediaRecorder() // if you are using MediaRecorder, release it first releaseCamera() // release the camera immediately on pause event } private fun releaseMediaRecorder() { mediaRecorder?.reset() // clear recorder configuration mediaRecorder?.release() // release the recorder object mediaRecorder = null mCamera?.lock() // lock camera for later use } private fun releaseCamera() { mCamera?.release() // release the camera for other applications mCamera = null } }
Java
public class CameraActivity extends Activity { private Camera mCamera; private SurfaceView preview; private MediaRecorder mediaRecorder; ... @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 (mediaRecorder != null) { mediaRecorder.reset(); // clear recorder configuration mediaRecorder.release(); // release the recorder object mediaRecorder = 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 级别 8)中可用,有关早期 API 版本中的等效调用,请参阅保存共享文件。Context.getExternalFilesDir
(Environment.DIRECTORY_PICTURES
) - 此方法返回与您的应用程序关联的保存图片和视频的标准位置。如果卸载您的应用程序,则此位置中的任何文件都将被删除。此位置的文件没有强制执行安全性,其他应用程序可能会读取、更改和删除它们。
以下示例代码演示了如何为媒体文件创建File
或Uri
位置,这些位置可以在使用Intent
调用设备的相机或作为构建相机应用程序的一部分时使用。
Kotlin
val MEDIA_TYPE_IMAGE = 1 val MEDIA_TYPE_VIDEO = 2 /** Create a file Uri for saving an image or video */ private fun getOutputMediaFileUri(type: Int): Uri { return Uri.fromFile(getOutputMediaFile(type)) } /** Create a File for saving an image or video */ private fun getOutputMediaFile(type: Int): File? { // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. val mediaStorageDir = 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 mediaStorageDir.apply { if (!exists()) { if (!mkdirs()) { Log.d("MyCameraApp", "failed to create directory") return null } } } // Create a media file name val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date()) return when (type) { MEDIA_TYPE_IMAGE -> { File("${mediaStorageDir.path}${File.separator}IMG_$timeStamp.jpg") } MEDIA_TYPE_VIDEO -> { File("${mediaStorageDir.path}${File.separator}VID_$timeStamp.mp4") } else -> null } }
Java
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
中,该内容 URI 是Intent
的一部分。
有关在 Android 设备上保存文件的更多信息,请参阅数据存储。
相机功能
Android 支持您可以使用相机应用程序控制的各种相机功能,例如图片格式、闪光灯模式、对焦设置等等。本节列出了常见的相机功能,并简要讨论了如何使用它们。大多数相机功能都可以通过Camera.Parameters
对象进行访问和设置。但是,有一些重要的功能需要比Camera.Parameters
中的简单设置更复杂。以下各节将介绍这些功能。
有关如何使用通过Camera.Parameters
控制的功能的常规信息,请查看使用相机功能部分。有关如何使用通过相机参数对象控制的功能的更详细信息,请按照以下功能列表中的链接跳转到 API 参考文档。
功能 | API 级别 | 描述 |
---|---|---|
人脸检测 | 14 | 识别图片中的人脸,并将其用于对焦、测光和白平衡。 |
测光区域 | 14 | 指定图像中一个或多个用于计算白平衡的区域。 |
对焦区域 | 14 | 设置图像中一个或多个用于对焦的区域。 |
白平衡锁定 |
14 | 停止或启动自动白平衡调整。 |
曝光锁定 |
14 | 停止或启动自动曝光调整。 |
视频快照 |
14 | 在拍摄视频时拍摄照片(帧抓取)。 |
延时视频 | 11 | 以设定的延迟录制帧以录制延时视频。 |
多个摄像头 |
9 | 支持设备上的多个摄像头,包括前置摄像头和后置摄像头。 |
对焦距离 |
9 | 报告相机与似乎处于对焦状态的对象之间的距离。 |
缩放 |
8 | 设置图像放大倍数。 |
曝光补偿 |
8 | 增加或减少光照曝光级别。 |
GPS 数据 |
5 | 在图像中包含或省略地理位置数据。 |
白平衡 |
5 | 设置白平衡模式,这会影响捕获图像中的颜色值。 |
对焦模式 |
5 | 设置相机如何对焦于某个主体,例如自动、固定、微距或无限远。 |
场景模式 |
5 | 为特定类型的摄影场景(例如夜景、沙滩、雪景或烛光场景)应用预设模式。 |
JPEG 质量 |
5 | 设置 JPEG 图像的压缩级别,这会增加或减少图像输出文件质量和大小。 |
闪光灯模式 |
5 | 打开或关闭闪光灯,或使用自动设置。 |
色彩效果 |
5 | 对捕获的图像应用色彩效果,例如黑白、棕褐色或负片。 |
抗频闪 |
5 | 减少由于 JPEG 压缩导致的颜色渐变中的频闪效果。 |
图片格式 |
1 | 指定图片的文件格式。 |
图片大小 |
1 | 指定保存的图片的像素尺寸。 |
注意:由于硬件差异和软件实现,并非所有设备都支持这些功能。有关检查您的应用程序正在运行的设备上功能可用性的信息,请参阅检查功能可用性。
检查功能可用性
在开始在 Android 设备上使用相机功能时,首先要了解的是,并非所有设备都支持所有相机功能。此外,支持特定功能的设备可能以不同的级别或不同的选项支持它们。因此,在开发相机应用程序时,您需要做出部分决策,即决定要支持哪些相机功能以及支持到什么程度。做出该决定后,您应该计划在相机应用程序中包含代码,以检查设备硬件是否支持这些功能,并在功能不可用时优雅地失败。
您可以通过获取相机参数对象的实例并检查相关方法来检查相机功能的可用性。以下代码示例显示了如何获取Camera.Parameters
对象并检查相机是否支持自动对焦功能。
Kotlin
val params: Camera.Parameters? = camera?.parameters val focusModes: List<String>? = params?.supportedFocusModes if (focusModes?.contains(Camera.Parameters.FOCUS_MODE_AUTO) == true) { // Autofocus mode is supported }
Java
// get Camera parameters Camera.Parameters params = camera.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()
方法,更改返回的参数对象,然后将其设置回相机对象来获得此对象,如下面的示例代码所示
Kotlin
val params: Camera.Parameters? = camera?.parameters params?.focusMode = Camera.Parameters.FOCUS_MODE_AUTO camera?.parameters = params
Java
// get Camera parameters Camera.Parameters params = camera.getParameters(); // set the focus mode params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // set Camera parameters camera.setParameters(params);
此技术适用于几乎所有相机功能,并且在获取 Camera
对象的实例后,几乎可以随时更改大多数参数。参数更改通常会立即在应用程序的相机预览中显示给用户。在软件方面,参数更改可能需要几个帧才能真正生效,因为相机硬件会处理新的指令,然后发送更新的图像数据。
重要提示:某些相机功能无法随意更改。特别是,更改相机预览的大小或方向需要先停止预览,更改预览大小,然后重新启动预览。从 Android 4.0(API 级别 14)开始,可以在不重新启动预览的情况下更改预览方向。
其他相机功能需要更多代码才能实现,包括
- 测光和对焦区域
- 人脸检测
- 延时视频
以下各部分提供了有关如何实现这些功能的简要概述。
测光和对焦区域
在某些摄影场景中,自动对焦和测光可能无法产生预期的结果。从 Android 4.0(API 级别 14)开始,您的相机应用程序可以提供其他控件,允许您的应用或用户指定图像中用于确定对焦或光照水平设置的区域,并将这些值传递给相机硬件以用于捕获图像或视频。
测光和对焦区域的工作方式与其他相机功能非常相似,因为您可以通过 Camera.Parameters
对象中的方法来控制它们。以下代码演示了为 Camera
的实例设置两个测光区域
Kotlin
// Create an instance of Camera camera = getCameraInstance() // set Camera parameters val params: Camera.Parameters? = camera?.parameters params?.apply { if (maxNumMeteringAreas > 0) { // check that metering areas are supported meteringAreas = ArrayList<Camera.Area>().apply { val areaRect1 = Rect(-100, -100, 100, 100) // specify an area in center of image add(Camera.Area(areaRect1, 600)) // set weight to 60% val areaRect2 = Rect(800, -1000, 1000, -800) // specify an area in upper right of image add(Camera.Area(areaRect2, 400)) // set weight to 40% } } camera?.parameters = this }
Java
// Create an instance of Camera camera = getCameraInstance(); // set Camera parameters Camera.Parameters params = camera.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); } camera.setParameters(params);
Camera.Area
对象包含两个数据参数:一个 Rect
对象,用于指定相机视野内的区域,以及一个权重值,用于告诉相机在测光或对焦计算中应给予此区域多大的重要程度。
Rect
字段在 Camera.Area
对象中描述了一个映射在 2000 x 2000 单位网格上的矩形形状。坐标 -1000、-1000 表示相机图像的左上角,坐标 1000、1000 表示相机图像的右下角,如下面的插图所示。
此坐标系的边界始终对应于相机预览中可见图像的外边缘,并且不会随着缩放级别而缩小或扩大。类似地,使用 Camera.setDisplayOrientation()
旋转图像预览不会重新映射坐标系。
人脸检测
对于包含人物的照片,人脸通常是照片中最重要的部分,在捕获图像时应使用人脸来确定对焦和白平衡。Android 4.0(API 级别 14)框架提供了用于识别面部并使用面部识别技术计算图片设置的 API。
注意:在面部检测功能运行期间,setWhiteBalance(String)
、setFocusAreas(List<Camera.Area>)
和 setMeteringAreas(List<Camera.Area>)
将不起作用。
在您的相机应用程序中使用面部检测功能需要几个通用步骤
- 检查设备上是否支持面部检测
- 创建面部检测监听器
- 将面部检测监听器添加到您的相机对象
- 在预览之后(以及每次重新启动预览之后)启动面部检测
并非所有设备都支持面部检测功能。您可以通过调用 getMaxNumDetectedFaces()
来检查此功能是否受支持。此检查的示例显示在下面的 startFaceDetection()
示例方法中。
为了接收面部检测事件的通知并做出响应,您的相机应用程序必须为面部检测事件设置监听器。为此,您必须创建一个实现 Camera.FaceDetectionListener
接口的监听器类,如下面的示例代码所示。
Kotlin
internal class MyFaceDetectionListener : Camera.FaceDetectionListener { override fun onFaceDetection(faces: Array<Camera.Face>, camera: Camera) { if (faces.isNotEmpty()) { Log.d("FaceDetection", ("face detected: ${faces.size}" + " Face 1 Location X: ${faces[0].rect.centerX()}" + "Y: ${faces[0].rect.centerY()}")) } } }
Java
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
对象,如下面的示例代码所示
Kotlin
camera?.setFaceDetectionListener(MyFaceDetectionListener())
Java
camera.setFaceDetectionListener(new MyFaceDetectionListener());
每次启动(或重新启动)相机预览时,您的应用程序都必须启动面部检测功能。创建一个用于启动面部检测的方法,以便您可以根据需要调用它,如下面的示例代码所示。
Kotlin
fun startFaceDetection() { // Try starting Face Detection val params = mCamera?.parameters // start face detection only *after* preview has started params?.apply { if (maxNumDetectedFaces > 0) { // camera supports face detection, so can start it: mCamera?.startFaceDetection() } } }
Java
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()
方法中,如下面的示例代码所示。
Kotlin
override fun surfaceCreated(holder: SurfaceHolder) { try { mCamera.setPreviewDisplay(holder) mCamera.startPreview() startFaceDetection() // start face detection feature } catch (e: IOException) { Log.d(TAG, "Error setting camera preview: ${e.message}") } } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { if (holder.surface == null) { // preview surface does not exist Log.d(TAG, "holder.getSurface() == null") return } try { mCamera.stopPreview() } catch (e: Exception) { // ignore: tried to stop a non-existent preview Log.d(TAG, "Error stopping camera preview: ${e.message}") } try { mCamera.setPreviewDisplay(holder) mCamera.startPreview() startFaceDetection() // re-start face detection feature } catch (e: Exception) { // ignore: tried to stop a non-existent preview Log.d(TAG, "Error starting camera preview: ${e.message}") } }
Java
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()
方法中启动面部检测,因为此时您的应用程序的执行过程中预览不可用。
延时视频
延时摄影视频允许用户创建将间隔几秒或几分钟拍摄的照片组合在一起的视频剪辑。此功能使用 MediaRecorder
来录制延时序列的图像。
要使用 MediaRecorder
录制延时摄影视频,您必须像录制普通视频一样配置录制器对象,将捕获的每秒帧数设置为较低数字,并使用其中一个延时摄影质量设置,如下面的代码示例所示。
Kotlin
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH)) mediaRecorder.setCaptureRate(0.1) // capture a frame every 10 seconds
Java
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher) mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH)); ... // Step 5.5: Set the video capture rate to a low number mediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds
这些设置必须作为 MediaRecorder
的更大配置过程的一部分进行。有关完整的配置代码示例,请参阅 配置 MediaRecorder。配置完成后,您可以像录制普通视频剪辑一样启动视频录制。有关配置和运行 MediaRecorder
的更多信息,请参阅 捕获视频。
Camera2Video 和 HdrViewfinder 示例进一步演示了本页介绍的 API 的用法。
需要权限的相机字段
在 Android 10(API 级别 29)或更高版本上运行的应用必须具有 CAMERA
权限才能访问 getCameraCharacteristics()
方法返回的以下字段的值
LENS_POSE_ROTATION
LENS_POSE_TRANSLATION
LENS_INTRINSIC_CALIBRATION
LENS_RADIAL_DISTORTION
LENS_POSE_REFERENCE
LENS_DISTORTION
LENS_INFO_HYPERFOCAL_DISTANCE
LENS_INFO_MINIMUM_FOCUS_DISTANCE
SENSOR_REFERENCE_ILLUMINANT1
SENSOR_REFERENCE_ILLUMINANT2
SENSOR_CALIBRATION_TRANSFORM1
SENSOR_CALIBRATION_TRANSFORM2
SENSOR_COLOR_TRANSFORM1
SENSOR_COLOR_TRANSFORM2
SENSOR_FORWARD_MATRIX1
SENSOR_FORWARD_MATRIX2
其他示例代码
要下载示例应用,请参阅 Camera2Basic 示例 和 官方 CameraX 示例应用。