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
- 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" />
- 存储权限 - 如果您的应用面向 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
对象后,通过调用 Camera.release()
方法来释放它!如果您的应用没有正确释放相机,所有后续尝试访问相机的操作(包括您自己的应用)都将失败,并可能导致您的应用或其他应用被关闭。
检测相机硬件
如果您的应用没有使用清单声明明确要求相机,您应该在运行时检查相机是否可用。要执行此检查,请使用 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
实例来请求访问它(除非您使用 intent 访问相机)。
要访问主相机,请使用 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 相同。根据窗口大小和纵横比,您可能需要使用信箱式布局将宽屏相机预览适配到竖屏布局,反之亦然。
将预览放置在布局中
相机预览类(例如上一节所示的示例)必须与用于拍照或录制视频的其他用户界面控件一起放置在 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()
获取配置文件实例。对于 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
预览布局元素。使用您为连接预览指定的同一对象。
注意:您必须按此顺序调用这些
MediaRecorder
配置方法,否则您的应用将遇到错误,录制将失败。 - 准备 MediaRecorder - 通过调用
MediaRecorder.prepare()
使用提供的配置设置准备MediaRecorder
。 - 启动 MediaRecorder - 通过调用
MediaRecorder.start()
开始录制视频。
- 解锁相机 - 通过调用
- 停止录制视频 - 按顺序调用以下方法,以成功完成视频录制
- 停止 MediaRecorder - 通过调用
MediaRecorder.stop()
停止录制视频。 - 重置 MediaRecorder - 可选地,通过调用
MediaRecorder.reset()
从录制器中移除配置设置。 - 释放 MediaRecorder - 通过调用
MediaRecorder.release()
释放MediaRecorder
。 - 锁定相机 - 通过调用
Camera.lock()
锁定相机,以便将来的MediaRecorder
会话可以使用它。从 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 添加到 Intent
的 EXTRA_OUTPUT
中。
有关在 Android 设备上保存文件的更多信息,请参阅数据存储。
相机功能
Android 支持您可以使用相机应用控制的多种相机功能,例如图片格式、闪光灯模式、对焦设置等等。本节列出了常见的相机功能,并简要讨论了如何使用它们。大多数相机功能都可以通过 Camera.Parameters
对象进行访问和设置。但是,有一些重要功能需要比在 Camera.Parameters
中进行简单设置更多的工作。以下部分将介绍这些功能
有关如何使用通过 Camera.Parameters
控制的功能的一般信息,请参阅使用相机功能部分。有关如何使用通过相机参数对象控制的功能的更详细信息,请按照下面功能列表中的链接查阅 API 参考文档。
表 1. 按引入它们的 Android 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);
The Camera.Area
object contains two data parameters: A Rect
object for specifying an area within the camera's field of view and a weight value, which tells the camera what level of importance this area should be given in light metering or focus calculations.
The Rect
field in a Camera.Area
object describes a rectangular shape mapped on a 2000 x 2000 unit grid. The coordinates -1000, -1000 represent the top, left corner of the camera image, and coordinates 1000, 1000 represent the bottom, right corner of the camera image, as shown in the illustration below.
图 1. 红线表示在相机预览中指定 Camera.Area
的坐标系。蓝色框显示了 Rect
值为 333,333,667,667 的相机区域的位置和形状。
The bounds of this coordinate system always correspond to the outer edge of the image visible in the camera preview and do not shrink or expand with the zoom level. Similarly, rotation of the image preview using Camera.setDisplayOrientation()
does not remap the coordinate system.
人脸检测
For pictures that include people, faces are usually the most important part of the picture, and should be used for determining both focus and white balance when capturing an image. The Android 4.0 (API Level 14) framework provides APIs for identifying faces and calculating picture settings using face recognition technology.
Note: While the face detection feature is running, setWhiteBalance(String)
, setFocusAreas(List<Camera.Area>)
and setMeteringAreas(List<Camera.Area>)
have no effect.
Using the face detection feature in your camera application requires a few general steps
- 检查设备是否支持人脸检测
- 创建人脸检测监听器
- 将人脸检测监听器添加到您的相机对象
- 预览后(以及每次预览重新启动后)开始人脸检测
并非所有设备都支持人脸检测功能。您可以通过调用 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());
Your application must start the face detection function each time you start (or restart) the camera preview. Create a method for starting face detection so you can call it as needed, as shown in the example code below.
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(); } }
You must start face detection each time you start (or restart) the camera preview. If you use the preview class shown in Creating a preview class, add your startFaceDetection()
method to both the surfaceCreated()
and surfaceChanged()
methods in your preview class, as shown in the sample code below.
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()
之后调用此方法。不要尝试在相机应用主 Activity 的 onCreate()
方法中启动人脸检测,因为此时应用执行尚未准备好预览。
延时视频
延时视频允许用户创建将相隔几秒或几分钟拍摄的图片组合起来的视频片段。此功能使用 MediaRecorder
录制图像以生成延时序列。
To record a time lapse video with MediaRecorder
, you must configure the recorder object as if you are recording a normal video, setting the captured frames per second to a low number and using one of the time lapse quality settings, as shown in the code example below.
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
These settings must be done as part of a larger configuration procedure for MediaRecorder
. For a full configuration code example, see Configuring MediaRecorder. Once the configuration is complete, you start the video recording as if you were recording a normal video clip. For more information about configuring and running MediaRecorder
, see Capturing videos.
The Camera2Video and HdrViewfinder samples further demonstrate the use of the APIs covered on this page.
需要权限的相机字段
运行 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 示例应用。