相机
Android的框架提供对设备上各种相机和照相功能的支持,让您在应用程序中拍摄照片和视频。本文讨论了一种快速,简单的方法用来捕捉图像和视频,同时概述了一种高级方式来为用户创建自定义相机体验。
注意事项
在你的应用程序能够在Android电子设备上使用相机之前,你应该考虑几个问题,那就是你的应用程序打算如何使用这个硬件功能
- 相机需求的声明 - 使用相机功能对于你的应用程序来说是否很重要并且你不希望你的应用程序被安装在没有相机的机器上?如果是这样,那么你需要把相机需要声明在配置文件里。
- 快速拍照还是自定义相机 - 你的应用程序该如何使用相机?你是否只是对捕捉一个快照或者一个视频剪辑,或者你的应用程序希望提供一种使用相机的新的方式?获取快照或者剪辑,建议 使用已存在的相机应用。开发一个自定义相机,查看创建³³一个相机应用章节。
- 存储 - 是否你的应用生成的图片和视频只对对你的应用可见,或者是用来分享,这样的话,其他应用程序例如相册或者其他的多媒体和社交应用程序可以使用它们?你是否希望你的应用程序被卸载后,这些照片和视频仍然可用?查看保存多媒体文件章节来看看怎么实现这些选项对话。
基础
Android框架支持通过Camera
API或相机捕获图像和视频 Intent
。以下是相关课程:
- 这个类是控制设备摄像头的主要API。当您构建相机应用程序时,此类用于拍摄照片或视频。
- 此类用于向用户呈现实时相机预览。
- 该类用于记录相机的视频。
-
意图动作类型
MediaStore.ACTION_IMAGE_CAPTURE
或MediaStore.ACTION_VIDEO_CAPTURE
可用于捕获图像或视频而不直接使用Camera
对象。
Camera
SurfaceView
MediaRecorder
Intent
清单声明
在使用Camera API开始开发应用程序之前,您应该确保您的清单具有适当的声明,以允许使用相机硬件和其他相关功能。
- 相机许可 - 您的应用程序必须要求使用设备相机的权限。
<uses-permission android:name = “android.permission.CAMERA” />
-
注意:如果您是通过意图使用相机,则您的应用程序不需要请求此权限。
- 相机功能 - 您的应用程序还必须声明使用相机功能,例如:
<uses-feature android:name = “android.hardware.camera” />
有关相机功能的列表,请参阅清单 功能参考。
将相机功能添加到您的清单会导致Google Play阻止您的应用程序安装到不包括相机的设备或不支持您指定的相机功能。有关在Google Play中使用基于功能的过滤功能的详细信息,请参阅Google Play和基于功能的过滤。
如果您的应用程序可以使用相机或相机功能进行正确的操作,但不需要它,则应通过包含
android:required
属性将其指定在清单中,并将其设置为false
:<uses-feature android:name = “android.hardware.camera” android:required = “false” />
- 存储权限 - 如果应用程序将映像或视频保存到设备的外部存储(SD卡),则还必须在清单中指定。
<uses-permission android:name = “android.permission.WRITE_EXTERNAL_STORAGE” />
- 音频录制许可 - 用于录制带有视频捕获的音频,您的应用程序必须请求音频捕获权限。
<uses-permission android:name = “android.permission.RECORD_AUDIO” />
- 位置许可 - 如果您的应用程序使用GPS位置信息标记图像,则必须请求位置许可:
<uses-permission android:name = “android.permission.ACCESS_FINE_LOCATION” />
有关获取用户位置的更多信息,请参阅 位置策略。
使用现有的相机应用程序
在不需要大量额外代码的情况下,您可以快速地在应用程序中拍摄照片或视频,方法是使用它Intent
来调用现有的Android相机应用程序。相机意图通过现有的相机应用程序请求捕获图片或视频剪辑,然后将控制权返回给您的应用程序。本节介绍如何使用此技术捕获图像或视频。
调用摄像机意图的过程遵循以下一般步骤:
- 撰写相机意图 -
Intent
使用以下意图类型之一创建请求图像或视频的图像:MediaStore.ACTION_IMAGE_CAPTURE
- 从现有相机应用程序请求图像的意图动作类型。MediaStore.ACTION_VIDEO_CAPTURE
- 用于从现有相机应用程序请求视频的意图动作类型。
- 启动相机意图 - 使用该
startActivityForResult()
方法执行相机意图。启动意图后,相机应用程序用户界面出现在设备屏幕上,用户可以拍摄照片或视频。 - 接收意图结果 -
onActivityResult()
在应用程序中设置方法以从相机意图接收回调和数据。当用户完成拍摄照片或视频(或取消操作)时,系统会调用此方法。
图像捕获意图
使用相机意图捕获图像是使应用程序以最小编码拍摄图片的快速方法。图像捕获意图可以包括以下额外信息:
MediaStore.EXTRA_OUTPUT
- 此设置需要一个Uri
对象,指定要保存图片的路径和文件名。此设置是可选的,但强烈建议。如果不指定此值,相机应用程序会将所请求的图像以缺省名称保存在默认位置,并在返回的意图Intent.getData()
字段中指定。
以下示例演示如何构建图像捕获意图并执行它。getOutputMediaFileUri()
本示例中的方法是指保存媒体文件中显示的示例代码。
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100 ; 私人Uri fileUri ; @Override public void onCreate (Bundle savedInstanceState ){ super 。onCreate (savedInstanceState ); 的setContentView (ř 。布局。主); //创建意图拍照,并将控制返回到调用应用程序意图意图= 新意图(MediaStore 。ACTION_IMAGE_CAPTURE ); fileUri = getOutputMediaFileUri (MEDIA_TYPE_IMAGE ); //创建一个文件来保存图像 意图。putExtra (MediaStore 。EXTRA_OUTPUT ,fileURI所); //设置图像文件名 //启动图像捕获Intent startActivityForResult (intent ,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE ); }
当startActivityForResult()
执行该方法时,用户将看到相机应用程序界面。用户完成拍照(或取消操作)后,用户界面将返回到您的应用程序,您必须拦截onActivityResult()
方法以接收该意图的结果并继续执行应用程序。有关如何接收完成的意图的信息,请参阅接收摄像机意图结果。
视频捕获意图
使用相机意图捕获视频是使应用程序以最小编码拍摄视频的一种快速方式。视频捕获意图可以包括以下额外信息:
MediaStore.EXTRA_OUTPUT
- 此设置需要Uri
指定要保存视频的路径和文件名。此设置是可选的,但强烈建议。如果不指定此值,Camera应用程序将所请求的视频保存在默认位置,并使用默认名称,在返回的intent的Intent.getData()
字段中指定。MediaStore.EXTRA_VIDEO_QUALITY
- 对于最低质量和最小文件大小,此值可以为0,对于最高质量和较大文件大小,此值可以为0。MediaStore.EXTRA_DURATION_LIMIT
- 设置此值以限制正在捕获的视频的长度(以秒为单位)。MediaStore.EXTRA_SIZE_LIMIT
- 设置此值以限制正在捕获的视频的文件大小(以字节为单位)。
以下示例演示如何构建视频捕获意图并执行它。getOutputMediaFileUri()
本示例中的方法是指保存媒体文件中显示的示例代码。
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200 ; 私人Uri fileUri ; @Override public void onCreate (Bundle savedInstanceState ){ super 。onCreate (savedInstanceState ); 的setContentView (ř 。布局。主); //创建新的意图意图意图= 新意图(MediaStore 。ACTION_VIDEO_CAPTURE ); fileUri = getOutputMediaFileUri (MEDIA_TYPE_VIDEO ); //创建一个文件来保存视频的 意图。putExtra (MediaStore 。EXTRA_OUTPUT ,fileURI所); //设置图像文件名 意图。putExtra (MediaStore 。EXTRA_VIDEO_QUALITY ,1 ); //将视频图像质量设置为高 //启动视频捕获意图 startActivityForResult (intent ,CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE ); }
当startActivityForResult()
执行该方法时,用户将看到一个修改后的相机应用程序界面。在用户完成视频(或取消操作)之后,用户界面返回到您的应用程序,您必须拦截onActivityResult()
方法以接收该意图的结果并继续执行应用程序。有关如何接收完成的意图的信息,请参阅下一节。
接收摄像机意图结果
一旦构建并执行了图像或摄像机意图,您的应用程序必须配置为接收意图的结果。本节介绍如何从摄像机意图截取回调,以便您的应用程序可以进一步处理捕获的图像或视频。
为了收到意图的结果,您必须覆盖onActivityResult()
开始使用意图的活动。以下示例演示了如何覆盖onActivityResult()
以捕获上一节中显示的图像摄像机意图或摄像机意图的结果。
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 ){ //图像捕获并保存到Intent Toast中指定的fileUri 。makeText (此,“图像保存到:\ n”个+ 数据。的getData (), 吐司。LENGTH_LONG )。show (); } else if (resultCode == RESULT_CANCELED ){ //用户取消了图像捕获} else { //图像捕获失败,建议用户} } if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE ){ if (resultCode == RESULT_OK ){ //视频捕获并保存到Intent Toast中指定的fileUri 。makeText (此,“视频保存到:\ n” + 数据。的getData (),烤面包。LENGTH_LONG )。show (); } else if (resultCode == RESULT_CANCELED ){ //用户取消了视频捕获} else { //视频捕获失败,建议用户} } }
一旦您的活动获得了成功的结果,捕获的图像或视频就可以在指定的位置使您的应用程序访问。
构建相机应用程序
一些开发人员可能需要根据应用程序外观进行定制的相机用户界面,或提供特殊功能。创建定制的相机活动需要比使用意图更多的代码,但它可以为您的用户提供更引人注目的体验。
为应用程序创建自定义相机界面的一般步骤如下:
- 检测和访问相机 - 创建代码以检查相机的存在并请求访问。
- 创建预览类 - 创建扩展
SurfaceView
和实现SurfaceHolder
界面的摄像机预览类。该课程预览相机的实时图像。 - 构建预览布局 - 拥有相机预览类后,创建一个包含预览和所需用户界面控件的视图布局。
- 用于捕获的设置侦听器 - 为接口控件连接侦听器以响应用户操作(例如按下按钮)启动图像或视频捕获。
- 捕获和保存文件 - 设置用于捕获图片或视频的代码并保存输出。
- 释放相机 - 使用相机后,应用程序必须正确释放以供其他应用程序使用。
相机硬件是一个共享资源,必须仔细管理,以便您的应用程序不会与其他可能需要使用的应用程序相冲突。以下部分将讨论如何检测相机硬件,如何请求访问相机,如何捕获图片或视频以及如何在应用程序完成后释放相机。
注意:记住Camera
通过调用Camera.release()
您的应用程序完成使用它来释放对象!如果您的应用程序没有正确地释放相机,则所有后续访问相机的尝试(包括您自己的应用程序)都将失败,并可能导致您的或其他应用程序关闭。
检测相机硬件
如果您的应用程序没有特别要求使用清单声明的相机,则应检查相机是否在运行时可用。要执行此检查,请使用PackageManager.hasSystemFeature()
以下示例代码所示的方法:
/ **检查该设备带有相机* / 私人布尔checkCameraHardware (上下文语境){ 如果(背景。getPackageManager ()。hasSystemFeature (PackageManager 。FEATURE_CAMERA )){ //该设备带有相机返回真; } else { //此设备上的相机不返回false ; } }
Android设备可以有多个摄像头,例如用于摄影的后置摄像头和用于视频通话的前置摄像头。Android 2.3(API等级9)及更高版本允许您使用该Camera.getNumberOfCameras()
方法检查设备上可用的摄像机数量。
访问相机
如果您确定运行应用程序的设备具有摄像头,则必须通过获取实例来请求访问它Camera
(除非您正在使用访问摄像机的意图)。
要访问主摄像机,请使用该Camera.open()
方法,并确保捕获任何异常,如以下代码所示:
/ **获取Camera对象实例的安全方式。* / public static Camera getCameraInstance (){ Camera c = null ; 尝试{ c = 相机。open (); //尝试获取相机实例} catch (异常e ){ //相机不可用(在使用或不存在)} return c ; //如果相机不可用,则返回null }
注意:使用时请务必检查异常情况Camera.open()
。如果相机使用或不存在,则无法检查异常将导致您的应用程序被系统关闭。
在运行Android 2.3(API Level 9)或更高版本的设备上,您可以使用特定的摄像机访问 Camera.open(int)
。上面的示例代码将访问具有多个摄像头的设备上的第一个后置摄像头。
检查相机功能
获取摄像机的权限后,您可以使用该Camera.getParameters()
方法获取有关其功能的更多信息,并检查返回的Camera.Parameters
对象以获得支持的功能。使用API Level 9或更高版本时,请使用Camera.getCameraInfo()
以确定相机是否在设备的正面或背面以及图像的方向。
创建一个预览类
为了让用户有效地拍照或录像,他们必须能够看到设备相机看到什么。相机预览课程是SurfaceView
可以显示来自相机的实时图像数据,因此用户可以对图片或视频进行构图和捕获。
以下示例代码演示如何创建可以包含在View
布局中的基本相机预览类。该类实现SurfaceHolder.Callback
为了捕获用于创建和销毁视图的回调事件,这是分配相机预览输入所需的。
/ **一个基本的相机预览类* / public class CameraPreview 扩展SurfaceView 实现SurfaceHolder 。回调{ private SurfaceHolder mHolder ; 私人相机mCamera ; public CameraPreview (上下文上下文,相机相机){ super (context ); mCamera = 相机; //安装SurfaceHolder.Callback,以便在底层表面被创建和销毁时收到通知。 mHolder = getHolder (); mHolder 。addCallback (this ); //已弃用的设置,但在2.0版本之前的Android版本需要3.0 mHolder 。的setType (SurfaceHolder 。SURFACE_TYPE_PUSH_BUFFERS ); } public void surfaceCreated (SurfaceHolder holder ){ //表面已创建,现在告诉相机在哪里绘制预览。尝试{ mCamera 。setPreviewDisplay (holder ); mCamera 。startPreview (); } catch (IOException e ){ Log 。d (TAG ,“错误设置相机预览:” + ê 。的getMessage ()); } } public void surfaceDestroyed (SurfaceHolder holder ){ // empty。照顾在您的活动中释放相机预览。} public void surfaceChanged (SurfaceHolder holder ,int format ,int w ,int h ){ //如果您的预览可以更改或旋转,请在此处处理这些事件。//确保在调整大小或重新格式化之前停止预览。 如果(mHolder 。getSurface ()== 空){ //预览表面不存在返回; } //在进行更改之前停止预览尝试{ mCamera 。stopPreview (); } catch (Exception e ){ // ignore:试图停止不存在的预览} //设置预览大小,并在此处进行任何调整大小,旋转或重新格式化更改 //使用新设置开始预览尝试{ mCamera 。setPreviewDisplay (mHolder ); mCamera 。startPreview (); } catch (Exception e ){ Log 。d (TAG ,“错误开始相机预览:” + ê 。的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” 机器人:layout_height = “FILL_PARENT” > <的FrameLayout 机器人:ID = “@ + ID / camera_preview” 机器人:layout_width = “FILL_PARENT” 机器人:layout_height = “FILL_PARENT” 机器人: <按钮机器人:ID = “@ + ID / button_capture” 机器人:文本= “捕捉” 机器人:layout_width = “WRAP_CONTENT” 机器人:layout_height = “WRAP_CONTENT” 机器人:layout_gravity = “中心” /> </的LinearLayout>
在大多数设备上,相机预览的默认方向为横向。此示例布局指定了一个水平(横向)布局,下面的代码将应用程序的方向修正为横向。为了简化渲染相机预览,您应该将应用程序的预览活动方向更改为横向,方法是将以下内容添加到清单中。
<activity android:name = “.CameraActivity” android:label = “@ string / app_name” android:screenOrientation = “landscape” > <! - 配置此活动以使用横向 - > <intent-filter> <action android:name = “android.intent.action.MAIN” /> <category android:name = “android.intent.category.LAUNCHER” /> </ intent-filter> </ activity>
注意:相机预览不必处于横向模式。从Android 2.2(API 8级)开始,您可以使用该setDisplayOrientation()
方法设置预览图像的旋转。为了在用户重新定向手机时更改预览方向,在surfaceChanged()
预览类的方法中,首先停止预览,Camera.stopPreview()
然后更改方向,然后再次开始预览Camera.startPreview()
。
在您的相机视图的活动中,将您的预览类添加到FrameLayout
上述示例中所示的元素。您的相机活动还必须确保在暂停或关闭相机时释放相机。以下示例显示如何修改相机活动以附加“ 创建预览”类中显示的预览类。
public class CameraActivity extends Activity { 私人相机mCamera ; 私人CameraPreview mPreview ; @Override public void onCreate (Bundle savedInstanceState ){ super 。onCreate (savedInstanceState ); 的setContentView (ř 。布局。主); //创建一个Camera的实例 mCamera = getCameraInstance (); //创建我们的预览视图并将其设置为我们活动的内容。 mPreview = new CameraPreview (this ,mCamera ); 的FrameLayout 预览= (的FrameLayout )findViewById (ID 。camera_preview ); 预览。addView (mPreview ); } }
注意:所述的getCameraInstance()
在上述示例方法指的是在所示的示例性方法访问摄像机。
捕捉图片
一旦建立了一个预览类和一个视图布局来显示它,您就可以开始使用应用程序捕获图像。在应用程序代码中,您必须设置用户界面控件的侦听器,以便通过拍摄照片来响应用户操作。
为了检索图片,请使用该Camera.takePicture()
方法。该方法需要三个接收来自相机数据的参数。为了以JPEG格式接收数据,您必须实现Camera.PictureCallback
接口来接收图像数据并将其写入文件。以下代码显示了Camera.PictureCallback
保存从相机接收的图像的界面的基本实现。
private PictureCallback mPicture = new PictureCallback (){ @覆盖公共空隙onPictureTaken (字节[] 数据,摄像机相机){ 文件pictureFile = getOutputMediaFile (MEDIA_TYPE_IMAGE ); if (pictureFile == null ){ Log 。d (TAG ,“错误创建媒体文件,检查存储权限:” + ê 。的getMessage ()); 返回; } 尝试{ FileOutputStream fos = new FileOutputStream (pictureFile ); FOS 。写(数据); FOS 。close (); } catch (FileNotFoundException e ){ Log 。d (TAG ,“未找到文件:” + ê 。的getMessage ()); } catch (IOException e ){ Log 。d (TAG ,“访问文件时出错:” + ê 。的getMessage ()); } } };
触发通过调用Camera.takePicture()
方法捕获图像。以下示例代码显示了如何从一个按钮调用此方法View.OnClickListener
。
//收听者添加到捕捉按钮按钮captureButton = (按钮)findViewById (ID 。button_capture ); captureButton 。setOnClickListener (新景观。OnClickListener (){ @覆盖公共无效的onClick (视图v ){ //从相机图像 mCamera 。takePicture (空,空,mPicture ); } } );
注意:mPicture
以下示例中的成员引用了上面的示例代码。
注意:记住Camera
通过调用Camera.release()
您的应用程序完成使用它来释放对象!有关如何释放相机的信息,请参阅释放相机。
捕获视频
使用Android框架的视频捕获需要仔细管理Camera
对象和与MediaRecorder
该类的协调。录制视频时Camera
,您必须管理Camera.lock()
和Camera.unlock()
呼叫,以允许MediaRecorder
访问摄像头硬件,除了Camera.open()
和Camera.release()
呼叫。
注意:从Android 4.0(API级别14)开始,自动为您管理Camera.lock()
和Camera.unlock()
呼叫。
与使用设备摄像头拍摄照片不同,拍摄视频需要非常特别的呼叫顺序。您必须按照特定的执行顺序成功准备和捕获应用程序的视频,具体如下。
- 打开相机 - 使用
Camera.open()
以获得相机对象的实例。 - 连接预览 - 通过
SurfaceView
使用相机连接到相机来准备实时摄像机图像预览Camera.setPreviewDisplay()
。 - 开始预览 - 呼叫
Camera.startPreview()
开始显示实时摄像机图像。 - 开始录制视频 - 为了成功录制视频,必须完成以下步骤:
- 解锁相机 -
MediaRecorder
通过调用解锁相机以供使用Camera.unlock()
。 - 配置MediaRecorder - 按以下顺序调用以下
MediaRecorder
方法。有关更多信息,请参阅参考文档。MediaRecorder
setCamera()
- 将相机设置为用于视频捕获,使用应用程序的当前实例Camera
。setAudioSource()
- 设置音频源,使用MediaRecorder.AudioSource.CAMCORDER
。setVideoSource()
- 设置视频源,使用MediaRecorder.VideoSource.CAMERA
。- 设置视频输出格式和编码。对于Android 2.2(API 8级)及更高版本,请使用该
MediaRecorder.setProfile
方法,并使用配置文件实例CamcorderProfile.get()
。对于2.2之前的Android版本,您必须设置视频输出格式和编码参数:setOutputFormat()
- 设置输出格式,指定默认设置或MediaRecorder.OutputFormat.MPEG_4
。setAudioEncoder()
- 设置声音编码类型,指定默认设置或MediaRecorder.AudioEncoder.AMR_NB
。setVideoEncoder()
- 设置视频编码类型,指定默认设置或MediaRecorder.VideoEncoder.MPEG_4_SP
。
setOutputFile()
-getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()
在“ 保存媒体文件”部分中,使用示例方法设置输出文件。setPreviewDisplay()
- 指定SurfaceView
应用程序的预览布局元素。使用您为Connect Preview指定的相同对象。
注意:您必须按此顺序调用这些
MediaRecorder
配置方法,否则您的应用程序将遇到错误,并且录制将失败。 - 准备MediaRecorder -
MediaRecorder
通过调用准备提供的配置设置MediaRecorder.prepare()
。 - 启动MediaRecorder - 通过调用开始录制视频
MediaRecorder.start()
。
- 解锁相机 -
- 停止录制视频 - 按顺序调用以下方法,以成功完成录像:
- 停止MediaRecorder - 通过调用停止录制视频
MediaRecorder.stop()
。 - 重新设置MediaRecorder(可选)通过调用从记录器中删除配置设置
MediaRecorder.reset()
。 - 发布MediaRecorder -
MediaRecorder
通过调用释放MediaRecorder.release()
。 - 锁定相机 - 锁定相机,以便将来的
MediaRecorder
会话可以通过调用使用它Camera.lock()
。从Android 4.0(API级别14)开始,除非MediaRecorder.prepare()
调用失败,否则不需要此 呼叫。
- 停止MediaRecorder - 通过调用停止录制视频
- 停止预览 - 当您的活动完成使用相机后,请停止预览
Camera.stopPreview()
。 - 释放相机 - 释放相机,以便其他应用程序可以通过调用使用它
Camera.release()
。
注意:可以先使用MediaRecorder
相机预览,然后跳过此过程的前几个步骤。然而,由于用户通常更喜欢在开始录制之前看到预览,所以这里不讨论该过程。
提示:如果您的应用程序通常用于录制视频时,设置 setRecordingHint(boolean)
到true
之前,要启动您的预览。此设置可以帮助减少开始录制所需的时间。
配置MediaRecorder
使用MediaRecorder
类录制视频时,必须按照特定的顺序执行配置步骤,然后调用该MediaRecorder.prepare()
方法来检查和实现配置。以下示例代码演示如何正确配置和准备 MediaRecorder
视频录制类。
private boolean prepareVideoRecorder (){ mCamera = getCameraInstance (); mMediaRecorder = new MediaRecorder (); //步骤1:解锁并将相机设置为MediaRecorder mCamera 。unlock (); mMediaRecorder 。setCamera (mCamera ); //步骤2:设置源 mMediaRecorder 。setAudioSource (MediaRecorder 。的AudioSource 。CAMCORDER ); mMediaRecorder 。setVideoSource (MediaRecorder 。VideoSource 。CAMERA ); //步骤3:设置摄像机配置文件(需要API级别8或更高版本) mMediaRecorder 。setProfile (CamcorderProfile 。得到(CamcorderProfile 。QUALITY_HIGH )); //步骤4:设置输出文件 mMediaRecorder 。setOutputFile (getOutputMediaFile (MEDIA_TYPE_VIDEO 。)的toString ()); //步骤5:设置预览输出 mMediaRecorder 。setPreviewDisplay (mPreview 。getHolder ()。getSurface ()); //步骤6:准备配置的MediaRecorder try { mMediaRecorder 。prepare (); } catch (IllegalStateException e ){ Log 。d (TAG ,“IllegalStateException异常制备MediaRecorder:” + ê 。的getMessage ()); releaseMediaRecorder (); 返回假; } catch (IOException e ){ Log 。d (TAG , “IOException prepare MediaRecorder:” + e 。getMessage ()); releaseMediaRecorder (); 返回假; } return true ; }
在Android 2.2(API 8级)之前,您必须直接设置输出格式和编码格式参数,而不是使用CamcorderProfile
。此方法在以下代码中进行了说明:
//步骤3:设置输出格式和编码(针对API级别8之前的版本) mMediaRecorder 。setOutputFormat (MediaRecorder 。OUTPUTFORMAT 。MPEG_4 ); mMediaRecorder 。setAudioEncoder (MediaRecorder 。AudioEncoder 。DEFAULT ); mMediaRecorder 。setVideoEncoder (MediaRecorder 。视频编码。DEFAULT );
以下录像参数为MediaRecorder
默认设置,但您可能需要调整应用程序的这些设置:
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
启动和停止MediaRecorder
使用MediaRecorder
课程开始和停止视频录制时,您必须遵循以下列出的特定顺序。
- 用相机解锁
Camera.unlock()
- 配置
MediaRecorder
如上面的代码示例所示 - 开始录制使用
MediaRecorder.start()
- 录制视频
- 停止录制使用
MediaRecorder.stop()
- 释放媒体录音机
MediaRecorder.release()
- 使用相机锁定
Camera.lock()
以下示例代码演示如何使用相机和MediaRecorder
课程连接按钮以正确启动和停止视频录制。
注意:完成录像时,请勿松开相机,否则将停止预览。
private boolean isRecording = false ; //收听者添加到捕捉按钮按钮captureButton = (按钮)findViewById (ID 。button_capture ); captureButton 。setOnClickListener (新景观。OnClickListener (){ @覆盖公共无效的onClick (视图v ){ 如果(isRecording ){ //停止录制,松开相机 mMediaRecorder 。停止(); //停止录制 releaseMediaRecorder (); //释放MediaRecorder对象 mCamera 。lock (); //从MediaRecorder返回相机访问 //通知用户录制已经停止了 setCaptureButtonText (“Capture” ); isRecording = false ; } else { //初始化摄像机if (prepareVideoRecorder ()){ //相机可用并解锁,MediaRecorder已准备好,//现在您可以开始录制 mMediaRecorder 。start (); //通知用户录音已启动 setCaptureButtonText (“Stop” ); isRecording = true ; } else { //准备不起作用,释放相机 releaseMediaRecorder (); //通知用户} } } } );
注意:在上面的示例中,该prepareVideoRecorder()
方法引用了配置MediaRecorder中显示的示例代码。此方法负责锁定相机,配置和准备MediaRecorder
实例。
释放相机
相机是由设备上的应用程序共享的资源。您的应用程序可以在获取实例后使用相机Camera
,并且在应用程序停止使用时,必须特别小心释放相机对象,并且一旦应用程序暂停(Activity.onPause()
)就可以。如果您的应用程序没有正确地释放相机,则所有后续访问相机的尝试(包括您自己的应用程序)都将失败,并可能导致您的或其他应用程序关闭。
要释放Camera
对象的实例,请使用该Camera.release()
方法,如下面的示例代码所示。
公共课CameraActivity extends Activity { private Camera mCamera ; 私人SurfaceView mPreview ; private MediaRecorder mMediaRecorder ; ... @Override protected void onPause (){ super 。onPause (); releaseMediaRecorder (); //如果你正在使用MediaRecorder,松开第一 releaseCamera (); //立即释放在相机上暂停事件} private void releaseMediaRecorder (){ if (mMediaRecorder != null ){ mMediaRecorder 。reset (); //清除录音机配置 mMediaRecorder 。release (); //释放录音机对象 mMediaRecorder = null ; mCamera 。lock (); //锁相机供以后使用} } private void releaseCamera (){ if (mCamera != null ){ mCamera 。release (); //为其他应用程序发布相机 mCamera = null ; } } }
注意:如果您的应用程序没有正确地释放相机,则所有后续访问相机的尝试(包括您自己的应用程序)都将失败,并可能导致您或其他应用程序关闭。
保存媒体文件
用户创建的媒体文件(如图片和视频)应保存在设备的外部存储目录(SD卡)中,以节省系统空间,并允许用户在没有设备的情况下访问这些文件。有许多可能的目录位置来保存设备上的媒体文件,但是您只能将两个标准位置视为开发人员:
Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES
) - 此方法返回保存图片和视频的标准,共享和推荐位置。该目录是共享的(public),所以其他应用程序可以轻松地发现,读取,更改和删除保存在此位置的文件。如果您的应用程序被用户卸载,保存到此位置的媒体文件将不会被删除。为了避免干扰用户现有的图片和视频,您应该为该目录中的应用程序的媒体文件创建一个子目录,如下面的代码示例所示。此方法在Android 2.2(API 8级)中可用,对于早期API版本中的等效调用,请参阅保存共享文件。Context.getExternalFilesDir
(Environment.DIRECTORY_PICTURES
) - 此方法返回用于保存与应用程序相关联的图片和视频的标准位置。如果您的应用程序被卸载,则保存在此位置的所有文件都将被删除。对该位置的文件不执行安全性,其他应用程序可能会读取,更改和删除它们。
以下示例代码演示了如何为媒体文件创建File
或Uri
位置,该媒体文件可以在使用Intent
或构建相机应用程序的一部分时调用设备的相机时使用。
public static final int MEDIA_TYPE_IMAGE = 1 ; public static final int MEDIA_TYPE_VIDEO = 2 ; / **创建文件Uri保存图像或视频* / private static Uri getOutputMediaFileUri (int type ){ return Uri 。fromFile (getOutputMediaFile (type )); } / **创建用于保存图像或视频的文件* / private static File getOutputMediaFile (int type ){ //为了安全起见,您应该在执行此操作之前检查使用Environment.getExternalStorageState()的SDCard是否已装入。 文件mediaStorageDir = 新文件(环境。getExternalStoragePublicDirectory (环境。DIRECTORY_PICTURES ),“MyCameraApp” ); //如果您希望创建的图像在应用程序之间共享//并在应用程序卸载后持续,则此位置最适合。 //创建存储目录,如果它不存在,如果(!mediaStorageDir 。存在()){ 如果(!mediaStorageDir 。mkdirs ()){ 登录。d (“MyCameraApp” ,“创建目录失败” ); 返回null ; } } //创建媒体文件名String timeStamp = new SimpleDateFormat (“yyyyMMdd_HHmmss” )。format (new Date ()); 文件媒体文件; 如果(类型== MEDIA_TYPE_IMAGE ){ 媒体文件= 新文件(mediaStorageDir 。的getPath ()+ 文件。隔板+ “IMG_” + 的timeStamp + “.JPG” ); } else if (类型== MEDIA_TYPE_VIDEO ){ 媒体文件= 新文件(mediaStorageDir 。的getPath ()+ 文件。隔板+ “VID_” + 的timeStamp + ”的.mp4" ); } else { return null ; } 返回mediaFile ; }
注意: Environment.getExternalStoragePublicDirectory()
在Android 2.2(API级别8)或更高版本中可用。如果您使用的是早期版本的Android设备,请Environment.getExternalStorageDirectory()
改用。有关详细信息,请参阅保存共享文件。
有关在Android设备上保存文件的更多信息,请参阅数据存储。
相机功能
Android支持各种相机功能,您可以使用相机应用程序进行控制,如图片格式,闪光模式,对焦设置等等。本节列出了常见的相机功能,并简要讨论了如何使用它们。可以使用通过Camera.Parameters
对象来访问和设置大多数摄像头功能。但是,有几个重要的功能需要更多的简单设置Camera.Parameters
。以下部分将介绍这些功能:
有关如何使用受控的功能的一般信息Camera.Parameters
,请查看使用相机功能部分。有关如何使用通过摄像机参数对象控制的功能的更多详细信息,请参见下面的功能列表中的链接到API参考文档。
特征 | API级别 | 描述 |
---|---|---|
人脸检测 | 14 | 识别图片中的人脸,并将其用于对焦,测光和白平衡 |
计量区域 | 14 | 指定图像中的一个或多个区域以计算白平衡 |
重点领域 | 14 | 设置图像中的一个或多个区域以用于焦点 |
White Balance Lock | 14 | 停止或开始自动白平衡调整 |
Exposure Lock | 14 | 停止或开始自动曝光调整 |
Video Snapshot | 14 | 拍摄视频时拍摄照片(框架抓取) |
时间流逝视频 | 11 | 记录设置延迟的帧以记录时间延迟视频 |
Multiple Cameras | 9 | 支持设备上的多台摄像机,包括前置摄像头和后置摄像头 |
Focus Distance | 9 | 报告相机和对象之间的距离 |
Zoom | 8 | 设置图像放大 |
Exposure Compensation | 8 | 增加或减少曝光量 |
GPS Data | 五 | 包含或省略地理位置数据与图像 |
White Balance | 五 | 设置白平衡模式,影响拍摄图像中的颜色值 |
Focus Mode | 五 | 设置相机如何对焦于诸如自动,固定,宏或无限远的主题 |
Scene Mode | 五 | 对特定类型的摄影情况(如夜间,海滩,雪景或烛光场景)应用预设模式 |
JPEG Quality | 五 | 设置JPEG图像的压缩级别,可增加或减少图像输出文件的质量和大小 |
Flash Mode | 五 | 打开,关闭闪光灯,或使用自动设置 |
Color Effects | 五 | 对拍摄的图像应用颜色效果,如黑白色,棕褐色调或阴性。 |
Anti-Banding | 五 | 由于JPEG压缩,降低了色彩渐变的效果 |
Picture Format | 1 | 指定图片的文件格式 |
Picture Size | 1 | 指定保存图片的像素尺寸 |
注意:由于硬件差异和软件实现,所有设备都不支持这些功能。有关检查应用程序运行的设备上功能的可用性的信息,请参阅检查功能可用性。
检查功能可用性
在Android设备上设置使用相机功能时,首先要了解的是,并非所有设备都支持所有相机功能。此外,支持特定功能的设备可能会将其支持到不同的级别或不同的选项。因此,您开发相机应用程序的部分决策过程是决定要支持哪些相机功能以及什么级别。做出决定后,您应该计划将相关应用程序中的代码包括在内,以检查设备硬件是否支持这些功能,如果功能不可用,则会正常失败。
您可以通过获取相机参数对象的实例来检查相机功能的可用性,并检查相关方法。以下代码示例显示了如何获取Camera.Parameters
对象并检查相机是否支持自动对焦功能:
//获取相机参数相机。参数params = mCamera 。getParameters (); 列表< 字符串> focusModes = PARAMS 。getSupportedFocusModes (); 如果(focusModes 。包含(相机。参数。FOCUS_MODE_AUTO )){ //自动对焦模式支持}
您可以使用上面显示的技术来获得大多数相机功能。所述 Camera.Parameters
对象提供一个getSupported...()
,is...Supported()
或getMax...()
方法,以确定是否(以及在何种程度)的特征的支持。
如果您的应用程序需要某些相机功能才能正常运行,则可以通过添加应用程序清单来要求它们。当您声明使用特定的相机功能(例如闪光灯和自动对焦)时,Google Play将您的应用程序限制为不支持这些功能的设备上。有关可以在应用程序清单中声明的摄像头功能的列表,请参阅清单 功能参考。
使用相机功能
大多数相机功能都使用Camera.Parameters
对象进行激活和控制。您可以通过首先获取对象的实例来获取此对象Camera
,调用该getParameters()
方法,更改返回的参数对象,然后将其设置回相机对象,如以下示例代码所示:
//获取相机参数相机。参数params = mCamera 。getParameters (); //设置焦点模式参数。setFocusMode (相机。参数。FOCUS_MODE_AUTO ); //设置摄像机参数 mCamera 。setParameters (params );
这种技术适用于几乎所有的相机功能,并且大多数参数可以在获取Camera
对象的实例之后随时更改。在应用程序的相机预览中,用户对参数的更改通常是可见的。在软件方面,参数更改可能需要几帧才能实际生效,因为相机硬件处理新指令,然后发送更新的图像数据。
重要提示:某些相机功能无法随意更改。特别是,更改相机预览的大小或方向需要先停止预览,更改预览大小,然后重新启动预览。从Android 4.0(API级别14)开始,预览方向可以更改,无需重新启动预览。
其他相机功能需要更多的代码才能实现,其中包括:
- 计量和重点领域
- 面部检测
- 时间延迟视频
以下部分提供了如何实现这些功能的快速概述。
计量和重点领域
在某些摄影场景中,自动对焦和测光可能无法产生预期的效果。从Android 4.0(API 14级)开始,您的相机应用程序可以提供额外的控件,允许您的应用或用户指定图像中用于确定焦点或光线级别设置的区域,并将这些值传递到相机硬件以用于捕获图像或视频。
用于测量和对焦的区域与其他摄像机功能非常相似,您可以通过Camera.Parameters
对象中的方法来控制它们。以下代码演示了如下设置两个测光区域 Camera
:
//创建一个Camera的实例 mCamera = getCameraInstance (); //设置相机参数相机。参数params = mCamera 。getParameters (); 如果(PARAMS 。getMaxNumMeteringAreas ()> 0 ){ //检查测光区域被支持列表< 相机。area > meteringAreas = new ArrayList < Camera 。区域>(); RECT areaRect1 = 新的Rect ( - 100 ,- 100 ,100 ,100 ); //指定图像 计量中心的区域。加入(新相机。区(areaRect1 ,600 )); //设置重量至60%的Rect areaRect2 = 新的Rect (800 ,- 1000 ,1000 ,- 800 ); //指定图像右上角的区域 meteringAreas 。加入(新相机。区(areaRect2 ,400 )); //将重量设置为40%的参数。setMeteringAreas (meteringAreas ); } mCamera 。setParameters (params );
该Camera.Area
对象包含两个数据参数:Rect
用于指定摄像机视野内的区域的对象和权重值,该值指示摄像机在光测量或聚焦计算中应该给出该区域的重要程度。
对象中的Rect
字段Camera.Area
描述映射在2000 x 2000单位格网上的矩形。坐标-1000,-1000表示相机图像的左上角,坐标1000,1000表示相机图像的右下角,如下图所示。
该坐标系的边界总是对应于在相机预览中可见的图像的外边缘,并且不缩小或缩放级别展开。类似地,使用的图像预览的旋转Camera.setDisplayOrientation()
不会重新映射坐标系。
面部检测
对于包含人物的图片,脸部通常是图片中最重要的部分,并且应在捕获图像时用于确定对焦和白平衡。Android 4.0(API Level 14)框架提供用于识别人脸和使用脸部识别技术计算图像设置的API。
注意:当脸部检测功能正在运行时 setWhiteBalance(String)
, 并且 没有任何效果。setFocusAreas(List)
setMeteringAreas(List)
使用相机应用程序中的人脸检测功能需要几个常规步骤:
- 检查设备是否支持人脸检测
- 创建一个面部检测监听器
- 将面部检测侦听器添加到您的相机对象
- 预览后启动脸部检测(每次预览重启后)
所有设备都不支持人脸检测功能。您可以通过调用来检查此功能是否受支持getMaxNumDetectedFaces()
。此检查的startFaceDetection()
示例显示在下面的示例方法中。
为了得到通知并对脸部的检测做出响应,您的相机应用程序必须设置一个用于人脸检测事件的侦听器。为此,您必须创建一个实现Camera.FaceDetectionListener
接口的监听器类,如下面的示例代码所示。
MyFaceDetectionListener 类实现相机。FaceDetectionListener { @覆盖公共空隙onFaceDetection (脸部[] 面,摄像相机){ 如果(面。长度> 0 ){ 日志。d (“人脸检测” ,“检测到的面部:” + 面。长度+ “脸部1地点X:” + 面[ 0 ] 的rect 。的centerX ()+ “Y:” + 面[ 0 ]。 RECT 。centerY ()); } } }
创建此类之后,然后将其设置为应用程序的 Camera
对象,如下面的示例代码所示:
mCamera 。setFaceDetectionListener (new MyFaceDetectionListener ());
每次启动(或重启)相机预览时,应用程序都必须启动人脸检测功能。创建一种开始面部检测的方法,以便您可以根据需要调用它,如下面的示例代码所示。
public void startFaceDetection (){ //尝试启动人脸检测相机。参数params = mCamera 。getParameters (); // *只后开始面部检测*预览已经开始,如果(PARAMS 。getMaxNumDetectedFaces ()> 0 {)//相机支持人脸检测,所以可以启动它: mCamera 。startFaceDetection (); } }
每次启动(或重新启动)相机预览时,都必须开始面部检测。如果您使用创建预览类中显示的预览类,请将您的startFaceDetection()
方法添加到预览类中的 方法 surfaceCreated()
和surfaceChanged()
方法中,如下面的示例代码所示。
public void surfaceCreated (SurfaceHolder holder ){ try { mCamera 。setPreviewDisplay (holder ); mCamera 。startPreview (); startFaceDetection (); //开始脸部检测功能 } catch (IOException e ){ Log 。d (TAG ,“错误设置相机预览:” + ê 。的getMessage ()); } } public void surfaceChanged (SurfaceHolder holder ,int format ,int w ,int h ){ 如果(mHolder 。getSurface ()== 空){ //预览表面不存在日志。d (TAG ,“mHolder.getSurface()== null” ); 返回; } 尝试{ mCamera 。stopPreview (); } catch (Exception e ){ //忽略:试图停止不存在的预览日志。d (TAG ,“错误停止相机预览:” + ê 。的getMessage ()); } 尝试{ mCamera 。setPreviewDisplay (mHolder ); mCamera 。startPreview (); startFaceDetection (); //重新开始脸部检测功能 } catch (Exception e ){ //忽略:试图停止不存在的预览日志。d (TAG ,“错误开始相机预览:” + ê 。的getMessage ()); } }
注:请记住调用此方法后调用 startPreview()
。不要尝试在onCreate()
相机应用程序主要活动的方法中启动人脸检测,因为预览在应用程序的执行过程中无法使用。
时间延迟视频
时间延迟视频允许用户创建视频剪辑,将组合几秒或几分钟的照片组合起来。该功能用于MediaRecorder
记录时间序列的图像。
要录制时间延迟视频MediaRecorder
,您必须配置录像机对象,就像录制正常视频一样,将每秒拍摄的帧数设置为低数字,并使用其中一个时间流逝的质量设置,如下面的代码示例所示。
//步骤3:设置摄像机配置文件(需要API级别8或更高版本) mMediaRecorder 。setProfile (CamcorderProfile 。得到(CamcorderProfile 。QUALITY_TIME_LAPSE_HIGH )); ... //步骤5.5:将视频捕获率设置为低数量的 mMediaRecorder 。setCaptureRate (0.1 ); //每10秒钟捕获一帧
这些设置必须作为更大的配置过程的一部分来完成MediaRecorder
。有关完整配置代码示例,请参阅配置MediaRecorder。配置完成后,您开始录像,就像录制正常视频剪辑一样。有关配置和运行的更多信息MediaRecorder
,请参阅捕获视频。