Android camera2官方API文档中文翻译
来源:互联网 发布:海岛奇兵能量神像数据 编辑:程序博客网 时间:2024/05/17 00:50
英文水平有限,官方API文档实在是看着眼晕,在机翻的基础上又自己翻译了一下,翻译的还是不太通顺但是瞅着能舒服一点…嘿嘿(^▽^)
相机
- 相机
- 注意事项
- 基础
- Manifest声明
- 使用现有的Camera Apps
- 图像捕捉Intent
- 视频采集Intent
- 接收camera intent的result
- 构建一个Camera App
- 检测相机硬件
- 访问相机
- 检查相机的特性
- 创建一个预览类
- 在布局中放置预览
- 捕捉照片
- 捕捉视频
- 配置MediaRecorder
- 启动停止MediaRecorder
- 释放Camera
- 保存媒体文件
- Camera特性
- 检查特性可用性
- 应用相机特性
- 测量与聚焦
- 人脸检测
- 延时视频
Android框架包括对各种设备的摄像头和相机功能的支持,允许你在应用程序中捕捉图片和视频。本文讨论了一种快速、简单的图像采集和视频捕获的方法,并概述了一种先进的方法来为你的用户创建自定义的相机体验。
注意事项
在让你的应用程序在Android设备上使用摄像头之前,考虑一下你的应用程序应该如何使用这个硬件的功能。
相机的要求——使用相机对你的应用非常重要,你希望你的应用安装在没有摄像头的设备上吗?如果是,您应该在您的manifest中声明相机需求。
快速图片或定制相机——你的应用程序将如何使用相机?你是否只对快速拍照或视频剪辑感兴趣,或者你的应用程序是否提供了一种使用相机的新方法?为了获得一个快速的拍照或剪辑,可以考虑使用现有的相机应用程序。要开发一个定制的相机功能,请查看构建一个相机应用程序部分。
存储——您的应用程序生成的图像或视频仅用于您的应用程序还是共享以便其他应用程序(如Gallery或其他媒体和社交应用程序)可以使用它们。你是否希望图片和视频仍然可用即使你的应用被卸载了。查看保存媒体文件部分,了解如何实现这些选项。
基础
Android类库通过android.hardware.camera2的API以及camera Intent来支持图像和视频的采集。以下是相关的类:
android.hardware.camera2
这个包是控制设备摄像头的主要API。当你构建一个相机应用的时候它可以用来拍照或者摄像。
Camera
这个类是用于控制设备摄像机的过时的API。
SurfaceView
这个类用于向用户呈现一个实时的摄像机预览。
MediaRecorder
这个类用来记录摄像机的视频。
Intent
一种MediaStore.ACTION_IMAGE_CAPTURE或MediaStore.ACTION_VIDEO_CAPTURE类型的intent action来捕获图像或视频,而无需直接使用Camera对象。
Manifest声明
在使用Camera API开始开发应用程序之前,您应该确保您的Manifest有适当的声明,以便允许使用相机硬件和其他相关特性。
相机许可——你的应用必须请求允许使用设备摄像头。
<uses-permission android:name="android.permission.CAMERA"/>
注意:如果您使用相机的Intent,您的应用程序不需要请求此权限。
相机功能——你的应用必须声明使用相机功能,例如:
<uses-feature android:name="android.hardware.camera"/>
对于一个相机功能列表,请参见清单功能引用。
将相机特性添加到您的清单中会导致谷歌播放,以防止您的应用程序被安装到不包含相机或不支持您指定的相机功能的设备上。有关使用基于特性的过滤与谷歌游戏的更多信息,请参见谷歌Play和基于特征的过滤。
如果您的应用程序可以使用相机或相机功能进行适当的操作,但不需要它,那么您应该在清单中指定该功能,包括android:required属性,并将其设置为false:<uses-feature android:name="android.hardware.camera" android:="false"/>
存储权限——如果您的应用程序将图像或视频保存到设备的外部存储器(SD卡)中,您还必须在清单中指定它。
<uses-permission android:name=" android.permission.WRITE_EXTERNAL_STORAGE"/>
音频录制许可——用于录制视频录制音频,您的应用程序必须请求音频捕获许可。
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
位置许可——如果你的应用程序标签图像与GPS位置信息,你必须请求位置允许:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
有关获取用户位置的更多信息,请参见位置策略。
使用现有的Camera Apps
一个快速的方法,在你的应用程序中拍照或视频,不需要很多额外的代码来使用Intent调用一个现有的Android相机应用。相机的Intent是通过一个现有的相机应用程序来捕捉图片或视频剪辑,然后将控制权返还给你的应用程序。本节将向您展示如何使用此技术捕获图像或视频。
调用相机Intent的过程遵循以下几个基本步骤:
编写一个Camera Intent——创建一个Intent,请求一个图像或视频,使用这些Intent类型之一:
- MediaStore.ACTION_IMAGE_CAPTURE - Intent动作类型,用于从现有的相机应用程序请求图像。
- MediaStore.ACTION_VIDEO_CAPTURE - Intent动作类型,用于从现有的摄像机应用程序请求视频。
启动Camera Intent——使用 startActivityForResult()方法来执行相机的intent。在您启动intent之后,摄像头应用程序用户界面出现在设备屏幕上,用户可以拍照或视频。
接收Camera Intent——在应用程序中设置一个onActivityResult()方法来接收来自相机intent的回调和数据。当用户完成拍照或视频(或取消操作)时,系统调用此方法。
图像捕捉Intent
使用相机的Intent捕获图像是一种快速的方法,可以使您的应用程序以最少的编码进行拍照。图像捕获Intent可以包括以下额外信息:
- MediaStore.EXTRA_OUTPUT—这个设置需要一个Uri对象,指定要保存图片的路径和文件名。这个设置是可选的,但强烈推荐。如果您没有指定此值,则相机应用程序将请求的图片保存在默认位置,并在返回Intent的Intent.getData()字段中指定默认名称。
下面的示例演示如何构造图像捕获Intent并执行它。本例中的getOutputMediaFileUri()方法引用了保存媒体文件中显示的示例代码。
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100; private Uri fileUri; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // create Intent to take a picture and return control to the calling application Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // create a file to save the image intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name // start the image capture Intent startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE); }
当执行startActivityForResult()方法时,用户会看到一个相机应用程序接口。在用户完成拍照(或取消操作)之后,用户界面会返回到您的应用程序,并且您必须拦截onActivityResult()方法来接收Intent的结果并继续执行应用程序。有关如何接收已完成Intent的信息,请参见接收Camera Intent Result。
视频采集Intent
使用相机的Intent捕获视频是一种快速的方法,可以让你的应用程序以最小的编码来拍摄视频。视频捕捉Intent可以包含以下额外信息:
- MediaStore.EXTRA_OUTPUT——这个设置要求Uri指定路径和文件名,以便保存视频。这个设置是可选的,但强烈推荐。如果您没有指定此值,则相机应用程序将请求的视频保存在默认位置,并在返回Intent的Intent.getData()字段中指定默认名称。
- MediaStore.EXTRA_VIDEO_QUALITY——这个值可以是最低质量的0和最小的文件大小,也可以是最高的质量和更大的文件大小。
- MediaStore.EXTRA_DURATION_LIMIT——设置这个值以限制视频被捕获的时间长度。
- MediaStore.EXTRA_SIZE_LIMIT——设置此值以限制捕获的视频的文件大小(以字节为单位)。
下面的示例演示如何构造视频采集intent并执行它。本例中的getOutputMediaFileUri()方法引用了保存媒体文件中显示的示例代码。
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200; private Uri fileUri; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //create new Intent Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO); // create a file to save the video intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // set the video image quality to high // start the Video Capture Intent startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE); }
当执行startActivityForResult()方法时,用户会看到一个修改后的相机应用程序接口。在用户完成视频(或取消操作)之后,用户界面会返回到您的应用程序,并且您必须拦截onActivityResult()方法来接收Intent的Result并继续执行应用程序。有关如何接收已完成Intent的信息,请参阅下一节。
接收camera intent的result
一旦您构造并执行了一个image或video camera的intent,您的应用程序必须配置为接收目标的result。本节将向您展示如何从camera intent中截取回调,这样您的应用程序就可以进一步处理捕获的图像或视频。
为了获得intent的result,您必须在启动intent的活动中重载onActivityResult()。下面的例子演示了如何重载onActivityResult(),以捕获前几节中显示的image camera intent或video camera intent示例的result。
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100; private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) { if (resultCode == RESULT_OK) { // Image captured and saved to fileUri specified in the Intent Toast.makeText(this, "Image saved to:\n" + data.getData(), Toast.LENGTH_LONG).show(); } else if (resultCode == RESULT_CANCELED) { // User cancelled the image capture } else { // Image capture failed, advise user } } if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) { if (resultCode == RESULT_OK) { // Video captured and saved to fileUri specified in the Intent Toast.makeText(this, "Video saved to:\n" + data.getData(), Toast.LENGTH_LONG).show(); } else if (resultCode == RESULT_CANCELED) { // User cancelled the video capture } else { // Video capture failed, advise user } } }
一旦您的活动收到一个成功的result,捕获的image或video就可以在您的应用程序的指定位置访问。
构建一个Camera App
一些开发人员可能需要一个相机用户界面,这些用户界面是根据他们的应用程序的外观定制的,或者提供特殊的功能。创建一个定制的相机活动需要更多的代码,而不是使用Intent,但是它可以为用户提供更有吸引力的体验。
注意:以下指南是针对较老的、已弃用的相机API。对于新的或高级的相机应用程序,推荐使用新的android.hardware.camera2的API。
为您的应用程序创建自定义相机界面的一般步骤如下:
- 检测和访问摄像机-创建代码检查是否存在摄像头和请求访问。
- 创建一个预览类——创建一个扩展了SurfaceView并实现了SurfaceHolder接口的相机预览类。这个类预览来自摄像机的实时图像。
- 构建一个预览布局——一旦你有了相机预览类,创建一个视图布局,它包含了你想要的预览和用户界面控件。
- 设置监听器为您的界面控件连接监听器,以启动图像或视频捕捉,以响应用户操作,如按下按钮。
- 捕获和保存文件-设置捕获图片或视频的代码并保存输出。
- 释放相机-在使用相机后,您的应用程序必须适当地释放它以供其他应用程序使用。
相机硬件是一个共享资源,必须仔细管理,这样您的应用程序才不会与其他可能想要使用它的应用程序发生冲突。下面几节讨论如何检测摄像头硬件,如何请求访问相机,如何捕捉图片或视频,以及如何在应用程序完成后释放摄像头。
注意:当您的应用程序使用它时,记得释放相机对象。如果您的应用程序没有适当地释放摄像机,所有后续尝试访问相机,包括您自己的应用程序,将会失败,并可能导致您或其他应用程序被关闭。
检测相机硬件
如果您的应用程序没有特别要求使用Manifest声明的camera,您应该检查在运行时是否有camera可用。要执行此检查,使用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 2.3(API Level 9),后来允许您检查可用摄像机的数量在设备上使用Camera.getNumberOfCameras()方法。
访问相机
如果你已经确定了你的应用程序运行的设备有一个摄像头,你必须请求通过获取一个Camera的实例来访问它(除非你正在使用一个Intent来访问相机)。
要访问主摄像机,请使用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级别9)或更高的设备上,您可以使用Camera.open(int)访问特定的摄像机。上面的示例代码将访问一个带有多个摄像头的设备上的第一个后置摄像头。
检查相机的特性
一旦您获得了对相机的访问,您可以使用Camera.getParameters()方法获得关于其功能的进一步信息,并检查返回的Camera.Parameters对象来查看支持的功能。当使用API级别9或更高时,使用Camera.getCameraInfo()来确定摄像头是否在设备的正面或背面,以及图像的方向。
创建一个预览类
为了让用户能够有效地拍照或拍摄视频,他们必须能够看到设备摄像头所看到的东西。相机预览类是一个可以显示来自相机的实时图像数据的SurfaceView,以便用户可以帧并捕捉图片或视频。
下面的示例代码演示了如何创建一个基本的相机预览类,它可以包含在视图布局中。这个类实现了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()方法中设置任意值。
在布局中放置预览
相机预览类,例如前一节所示的示例,必须与其他用户界面控件一起放置在活动的布局中,以获取图片或视频。本节将向您展示如何构建预览的基本布局和活动。
下面的布局代码提供了一个非常基本的视图,可以用来显示相机预览。在本例中,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>
在大多数设备上,相机预览的默认方向是风景。这个示例布局指定了一个横向(横向)布局,下面的代码修复了应用程序的方向。为了简单地呈现一个相机预览,您应该通过向Manifest添加以下内容来改变应用程序的预览活动方向。
<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()方法来设置预览图像的旋转。为了改变预览方向,当用户重新定位手机时,在preview类的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()方法来捕获图像。下面的示例代码显示了如何从button的 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.release()释放相机对象。有关如何释放相机的信息,请参阅释放Camera。
捕捉视频
使用Android框架的视频捕获需要对Camera对象进行细致的管理,并与MediaRecorder类进行协调。当用相机录制视频时,你必须管理Camera.lock()和Camera.unlock()的调用来允许MediaRecorder访问相机硬件,另外还有Camera.open()和Camera.release()。
注意:从Android 4.0(API级别14)开始,Camera.lock()和Camera.unlock()调用将自动为您管理。
不同于用设备相机拍照,拍摄视频需要一个非常特别的呼叫顺序。您必须遵循特定的执行顺序,以成功地为您的应用程序准备和捕获视频,如下所示。
- 打开相机——使用照相机。Open()获取相机对象的一个实例。
- 连接预览——准备一个实时的相机图像预览通过连接一个SurfaceView到相机使用Camera.setPreviewDisplay()。
- 开始预览——呼叫Camera.startPreview()开始显示现场摄像机图像。
开始录制视频——为了成功录制视频,必须完成以下步骤:
a. 解锁相机——解锁相机,为MediaRecorder通过呼叫Camera.unlock()。
b. 配置MediaRecorder——调用下面的MediaRecorder方法。有关更多信息,请参见
MediaRecorder参考文档。- setCamera()—设置用于视频捕获的摄像机,使用您的应用程序当前的相机实例。
- 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预览布局元素。使用您为Connect预览指定的相同对象。
注意:在这个顺序中,您必须调用这些MediaRecorder配置方法,否则您的应用程序将会遇到错误,而记录将会失败。
c. 准备MediaRecorder—通过调用mediarecorder.Prepare()来准备提供配置设置的MediaRecorder。
d. 启动MediaRecorder——通过调用MediaRecorder.Start()开始录制视频。
停止录制视频——按顺序调用以下方法,成功完成视频录制:
a. 停止MediaRecorder——通过调用MediaRecorder.Stop()停止记录视频。
b. 重置MediaRecorder——可选地,通过调用MediaRecorder.Reset(),从记录器中删除配置设置。
c. 释放MediaRecorder——通过调用mediarecorder.Release()来释放MediaRecorder。
d. 锁定Camera——锁定相机,以便将来的MediaRecorder会话可以通过调用Camera.lock()来使用。从Android 4.0开始(API Level 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类启动和停止视频记录时,您必须遵循特定的顺序,如下所示。
- 用Camera.unlock()解锁相机
- 按照上面的代码示例配置MediaRecorder
- 使用MediaRecorder.start()开始记录
- 记录视频
- 使用MediaRecorder.stop()停止记录
- 用MediaRecorder.release()释放媒体记录器
- 用Camera.lock()锁住相机
下面的示例代码演示如何使用摄像机和MediaRecorder类连接一个按钮以正确启动和停止视频录制。
注意:当完成一个视频录制时,不要释放摄像头,否则你的预览会被停止。
private boolean isRecording = false; // Add a listener to the Capture button Button captureButton = (Button) findViewById(id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if (isRecording) { // stop recording and release camera mMediaRecorder.stop(); // stop the recording releaseMediaRecorder(); // release the MediaRecorder object mCamera.lock(); // take camera access back from MediaRecorder // inform the user that recording has stopped setCaptureButtonText("Capture"); isRecording = false; } else { // initialize video camera if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, // now you can start recording mMediaRecorder.start(); // inform the user that recording has started setCaptureButtonText("Stop"); isRecording = true; } else { // prepare didn't work, release the camera releaseMediaRecorder(); // inform user } } } } );
注意:在上面的示例中,prepareVideoRecorder()方法引用了配置MediaRecorder的示例代码。该方法负责锁定摄像机,配置和准备MediaRecorder实例。
释放Camera
摄像头是设备上应用程序共享的资源。您的应用程序可以在获得一个相机实例后使用该相机,并且当您的应用程序停止使用时,您必须格外小心地释放该相机对象,并且当您的应用程序暂停(Activity.onPause())时。如果您的应用程序没有适当地释放摄像机,所有后续尝试访问相机,包括您自己的应用程序,将会失败,并可能导致您或其他应用程序被关闭。
要释放摄像机对象的一个实例,请使用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)——这个方法返回标准,分享和推荐位置保存图片和视频。该目录是共享的(public),因此其他应用程序可以轻松地发现、读取、更改和删除保存在此位置的文件。如果您的应用程序被用户卸载,保存到该位置的媒体文件将不会被删除。为了避免干扰用户现有的图片和视频,您应该为应用程序的媒体文件创建子目录,如下面的代码示例所示。这个方法在Android 2.2(API Level 8)中是可用的,在早期的API版本中,类似的调用可以看到保存共享文件。
Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES),该方法返回一个标准的位置保存图片和视频与您的应用程序。如果您的应用程序未安装,那么在此位置保存的任何文件都将被删除。此位置的文件不强制执行安全性,其他应用程序可能会读取、更改和删除它们。
下面的示例代码演示了如何为一个媒体文件创建一个文件或Uri位置,该文件可以用于调用带有Intent的设备的摄像机,或者作为构建相机应用程序的一部分。
public static final int MEDIA_TYPE_IMAGE = 1; public static final int MEDIA_TYPE_VIDEO = 2; /** Create a file Uri for saving an image or video */ private static Uri getOutputMediaFileUri(int type){ return Uri.fromFile(getOutputMediaFile(type)); } /** Create a File for saving an image or video */ private static File getOutputMediaFile(int type){ // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), "MyCameraApp"); // This location works best if you want the created images to be shared // between applications and persist after your app has been uninstalled. // Create the storage directory if it does not exist if (! mediaStorageDir.exists()){ if (! mediaStorageDir.mkdirs()){ Log.d("MyCameraApp", "failed to create directory"); return null; } } // Create a media file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); File mediaFile; if (type == MEDIA_TYPE_IMAGE){ mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg"); } else if(type == MEDIA_TYPE_VIDEO) { mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_"+ timeStamp + ".mp4"); } else { return null; } return mediaFile; }
注意:Environment.getExternalStoragePublicDirectory()可以在Android 2.2(API Level 8)或更高版本中使用。如果你是针对与早期版本的Android设备,请使用Environment.getExternalStorageDirectory()。有关更多信息,请参见保存共享文件。
有关在Android设备上保存文件的更多信息,请参见数据存储。
Camera特性
Android支持大量的相机功能,你可以通过相机应用来控制,比如图片格式,闪光模式,焦点设置,等等。本节列出了常见的相机功能,并简要讨论了如何使用它们。大多数相机功能都可以通过摄像头来访问和设置参数对象。但是,有几个重要的特性需要比相机中的简单设置更重要。这些特性包括以下部分:
- 计量和重点领域
- 人脸检测
- 时间流逝的视频
有关如何使用由摄像机控制的特性的一般信息。参数,使用相机功能部分。有关如何使用相机参数对象控制特性的更详细信息,请遵循下面的特性列表中的链接到API参考文档。
表1.常见的相机特性,按Android API级别进行排序。
注意:由于硬件差异和软件实现,部分设备不支持这些特性。有关检查应用程序正在运行的设备上的特性可用性的信息,请参见检查特性可用性。
检查特性可用性
首先要理解的是,在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…()方法来确定是否支持某个特性(以及在何种程度上)。
如果您的应用程序需要特定的相机功能来正常工作,您可以通过添加到您的应用程序清单来要求它们。当您声明使用特定的相机功能时,例如flash和自动聚焦,谷歌会限制您的应用程序在不支持这些特性的设备上安装。对于可以在应用程序清单中声明的相机功能列表,请参见manifest功能引用。
应用相机特性
大多数相机功能都是用Camera.Parameters对象来激活和控制的。首先获取一个Camera对象的实例,调用getParameters()方法,更改返回的参数对象,然后将其重新设置为Camera对象,如下面的示例代码所示:
// 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)
该技术适用于几乎所有的相机特性,并且大多数参数可以在获得相机对象实例后的任何时间进行修改。在应用程序的相机预览中,用户通常可以看到对参数的更改。在软件方面,参数的变化可能需要几个帧才能实际生效,这是因为摄像机硬件要处理新的指令,然后才能发送更新的图像数据。
Important:一些相机功能不能随意改变。特别地,改变相机预览的大小或方向要求您先停止预览,更改预览大小,然后重新启动预览。从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对象,用于指定摄像机视野范围内的区域和一个权重值,该对象告诉摄像机在光测量或焦点计算中该区域的重要性级别。
在Camera.Area对象中的Rect字段描述了一个2000 x 2000单元格。坐标 -1000,-1000 表示摄像机图像的左上角,坐标1000,1000表示摄像机图像的右下角,如图所示。
图1所示。红线说明了指定摄像机的坐标系统。在相机预览范围内的区域。蓝色方框显示的是一个摄像机区域的位置和形状,它的Rect值为333,333,667,667。
这个坐标系统的边界总是对应于相机预览中的图像的外边缘,不会随着缩放水平而缩小或扩大。同样,旋转图片的预览使用Camera.setDisplayOrientation()不重新映射坐标系统。
人脸检测
对于包括人在内的图片来说,脸通常是图片最重要的部分,在捕捉图像时应该被用来确定焦点和白色的平衡。Android 4.0(API Level 14)框架提供了使用人脸识别技术识别人脸和计算图片设置的API。
注意:当人脸检测功能正在运行时,setWhiteBalance(String)、setFocusAreas(List)和setMeteringAreas(List)都没有效果。
在相机应用程序中使用人脸检测功能需要几个基本步骤:
- 检查设备上是否支持人脸检测
- 创建一个面部检测监听器
- 将面部检测侦听器添加到您的camera对象中
- 预览后开始人脸检测(每次预览重新启动后)
不是所有设备都支持人脸检测功能。可以通过调用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的更多信息,请参见捕获视频。