相机
Android核心框架包括各种摄像机和照相机功能,在应用程序中允许你拍照和录像。本文讨论了一个快速,简单的拍照和摄影的方法,并概述为用户创建自定义相机的高级体验方法。
注意事项
在Android设备上的应用程序使用相机之前,你应该考虑您的应用程序打算如何使用这些硬件功能的几个问题。
- 相机需求 -对于你的应用程序来说是很重要的,你不希望你的应用安装在一个没有camera的设备上,如果是这样,你应该声明camera requirement in your manifest(请看本文的manifest声明)。
- 快拍或自定义相机 -您的应用程序将如何使用相机?你只对连续快拍或视频剪辑有兴趣,或者你的应用程序将提供一种新的方式来使用摄像机?对于要获得一个快拍或视频剪辑,可以考虑 使用现有的照相机应用程序。关于开发自定义的照相机功能,参阅创建相机应用章节。
- 存储 -你应用程序产生的图片和视频只是对你自己应用可用,或者是共享的,以便其他的应用程序如相册或其他多媒体和社交应用程序可以使用它们?你希望即使应用程序卸载了,照片和视频也是可用的?请参阅 Saving Media Files 章节。
基础知识
通过 android.hardware.camera2
API或相机Intent,Android框架支持拍照和录像
。以下是相关的类:
- 这个包是用于控制Camera设备的主要API。在你创建一个Camera应用程序时,它可以用来拍照或录像。
- 这个类是用于控制Camera设备的旧的过时的API。
- 这个类是用来给用户展示现场Camera预览。
- 此类用于从相机中录制视频。
-
MediaStore.ACTION_IMAGE_CAPTURE
或MediaStore.ACTION_VIDEO_CAPTURE
的Intent动作类型可以用来拍照或录像,而无需直接使用Camera
对象。
android.hardware.camera2
camera
SurfaceView
MediaRecorder
意图
manifest声明
在你用Camera API 开发你的应用程序之前,你应该确保你的manifest有相应的声明,从而允许使用摄像头硬件和其他相关功能。
- 相机权限 -您的应用程序必须请求使用摄像头设备的权限。
<uses-permissionandroid:name="android.permission.CAMERA"/>
注:如果你通过Intent使用相机,你的应用程序并不需要请求此权限。
- 相机功能 -您的应用程序还必须声明使用相机功能,例如:
<uses-featureandroid:name="android.hardware.camera"/>
对于相机功能的列表,请参阅清单 Features Reference。
增加Camera功能到你的manifest文件,因为Google Play 会阻止你的应用安装到某些设备上,而这些设备不支持Camera的功能。关于更多Google Play过滤使用基本功能的信息,请参阅 Google Play and Feature-Based Filtering。
如果您的应用程序可以正常使用照相机或摄像机功能,但不要求它,你应该在包含它manifest 文件中的增加
android:required
属性,并设置为“false”。<uses-featureandroid:name="android.hardware.camera"android:required="false"/>
- 存储权限 -如果你的应用程序保存相片或视频到设备的D卡上,还必须在manifest中注明。
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- 录音权限 -对于录像,应用程序必须请求音频采集权限。
<uses-permissionandroid:name="android.permission.RECORD_AUDIO"/>
- 位置权限 -如果你的应用程序标记图像有GPS位置信息,您必须请求位置的权限:
<uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION"/>
有关获取用户位置的详细信息,请参阅 Location Strategies.。
使用现有的相机应用
在你的应用程序中不用额外多的代码快速实现拍照和录像的方式是用Intent去请求一个现有的Camera应用程序。一个Camera 意图可以发送拍照或摄像的请求给一个现有的Camera应用程序,然后会将操作结果返回给你的应用程序,本章节就向你展示使用此技术如何实现拍照和录像。
用于调用摄像头意图的过程遵循以下一般步骤:
- 撰写相机意图 -创建一个
Intent
请求的拍照或录像,使用这些意图类型之一:MediaStore.ACTION_IMAGE_CAPTURE
-从现有Camera应用程序请求拍照的意图动作类型。MediaStore.ACTION_VIDEO_CAPTURE
-从现有Camera应用程序请求录像的意图动作类型。
- 启动相机意图 -使用
startActivityForResult()
方法来执行相机意图。启动的意图以后,相机应用程序的用户界面显示在设备屏幕上,用户可以拍照或录像。 - 接收意图结果 -在应用程序中建立
onActivityResult()
方法接收来Camera意图的回调和数据。当用户结束拍照或录像(或取消操作)时,系统调用此方法。
拍照意图(Image capture intent)
使用Camera Intent拍照是一种快速让你的应用程序以最少的代码实现拍照功能的方法。一个拍照Intent可能包含下面这些额外的信息。
MediaStore.EXTRA_OUTPUT
-此设置需要一个Uri
对象,指定
你想保存图片的路径和文件名 。此设置是可选的,但强烈建议使用。如果不指定此值,相机应用程序将把照片用一个默认的名称保存在默认路径中,在返回的意图的Intent.getData()
字段中指定。
下面的例子演示了如何构建一个拍照Intent并执行它。本例字getOutputMediaFileUri()方法参考于 Saving Media Files中的样例。
privatestaticfinalint CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE =100;
privateUri fileUri;
@Override
publicvoid 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 =newIntent(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,请参阅 Receiving camera intent result。
录像意图(Video capture intent)
使用Camera Intent录像是一种快速让你的应用程序以最少的代码实现录像功能的方法。录像意图可以包括以下额外信息:
MediaStore.EXTRA_OUTPUT
-此设置需要一个Uri
对象,指定
你想保存视频的路径和文件名 。此设置是可选的,但强烈建议使用。如果不指定此值,相机应用程序将把视频用一个默认的名称保存在默认路径中,在返回的意图的Intent.getData()
字段中指定。MediaStore.EXTRA_VIDEO_QUALITY
-这个值可以是0,表示最低的质量和最小的文件大小,或者是1表示最高质量和更大的文件大小。MediaStore.EXTRA_DURATION_LIMIT
-设置这个值,为了以秒为单位来限制视频录制的长度。MediaStore.EXTRA_SIZE_LIMIT
-设置这个值,为了以bytes为单位来限制录制视频的文件大小。
下面的例子演示了如何构建一个录像的Intent并执行它。本例字getOutputMediaFileUri()方法参考于 Saving Media Files中的样例。
privatestaticfinalint CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE =200;
privateUri fileUri;
@Override
publicvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//create new Intent
Intent intent =newIntent(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,请参阅下一节。
接收Camera Intent的结果(Receiving camera intent result)
你一旦构建并执行了拍照或录像意图,应用程序必须被配置去接收的意图的结果。本节将告诉您如何从相机意图拦截回调使您的应用程序可以进一步做拍照或录像的处理。
为了接收意图的结果,在开始的意图的activity中你必须重写onActivityResult()
。下面的例子演示了如何重写onActivityResult()来
捕捉到image camera intent或video camera intent 的结果,此例子在前面章节中有显示。
privatestaticfinalint CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE =100;
privatestaticfinalint CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE =200;
@Override
protectedvoid 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();
}elseif(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();
}elseif(resultCode == RESULT_CANCELED){
// User cancelled the video capture
}else{
// Video capture failed, advise user
}
}
}
一旦你的activity接收到一个成功的结果,你的应用程序去访问保存在指定地方的所拍的照片和视频是可行的。
创建一个相机应用程序(Building a Camera App)
一些开发人员可能需要定制自己的应用程序的外观或提供特殊功能的相机用户界面。创建自定义相机的activity需要比 using an intent更多的代码,但它可以提供一个更具吸引力的体验用户。
注意:以下指南是过时,废弃
API。对于新的或先进的相机应用程序,建议使用较新的Camera
android.hardware.camera2
API。
用于创建应用程序自定义相机界面的一般步骤如下:
- 检测和访问摄像机 -创建代码检查相机的存在并请求访问。
- 创建预览类 -创建一个继承于SurfaceView相机预览类,并实现
SurfaceHolder
接口。这个类可以用来从相机中预览实时图像。 - 建立预览布局 -一旦你创建了相机预览类,创建一个视图布局,这个布局包含预览界面和你想要用户控件的界面。
- 为捕获设置监听器 -为你的界面连接监听器,用来响应用户的操作去拍照或者录像,如按下一个按钮。
- 捕获和保存文件 -为拍照或录像,以及保存文件编写代码。
- 释放Camera -使用相机后,您的应用程序必须正确地释放它,以便其他应用程序使用。
Camera硬件是一种共享资源,你必须认真管理,因此你的应用程序不能跟其他应用程序同时使用它。以下各节讨论了如何检测相机的硬件,如何请求访问摄像头,如何拍照或录像,以及当你的应用程序使用它完成后,如何释放Camera,。
注意:请记住,当你的应用程序不再使用Camera后,要调用 Camera.release()
释放Camera对象!如果您的应用程序不能正确地释放Camera,所有后续尝试访问Camera的应用程序都会失败,并可能让你的应用程序或其他的应用程序强制关闭,其中包括你自己的应用程序。
检测Camera硬件
如果你的应用程序没有使用manifest声明你的应用程序需求camera,你应用在运行的时候检查Camera是否可用。为了执行这种检查,使用PackageManager.hasSystemFeature()
方法,如下面的示例代码:
/** Check if this device has a camera */
privateboolean checkCameraHardware(Context context){
if(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
returntrue;
}else{
// no camera on this device
returnfalse;
}
}
Android设备可以有多个摄像头,例如用于拍照的后置摄像头和用于视频通话的前置摄像头。Android 2.3(API等级9)及更高版本允许你使用Camera.getNumberOfCameras()
方法检查设备上使用相机的数量。
访问cameras
如果您已经确定在运行应用程序的设备上有一个Camera,你必须通过获取Camera的实例来访问它(除非你使用的是意图访问Camera)。
要访问的主Camera,使用Camera.open()
方法,并一定要捕获任何异常,如下面的代码:
/** A safe way to get an instance of the Camera object. */
publicstaticCamera 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时,检查异常Camera.open()
。如果不检查异常,如果Camera已被使用或不存在会导致应用程序被系统关闭。
设备上运行的Android 2.3(API等级9)或更高版本,您可以使用 Camera.open(INT)
访问特定相机。上面的例子中的代码访问的是第一个Camera-后置Camera,设备上可以具有一个以上的Camera。
检查Camera功能
一旦你获得访问摄像头,使用Camera.getParameters()
方法你可以得到有关使用其功能的进一步信息,并检查返回Camera.Parameters
对象的支持能力。当使用Level 9 的API或更高,使用Camera.getCameraInfo()来
确定是否一个照相机是在前面或在设备的背面,以及图像的显示方向。
创建预览类(Creating a preview class)
为了用户能有效地拍照或录像,他们必须能够看到设备相机所能捕获到的。一个Camera预览类是一个SurfaceView
,可以显示从相机中捕获到的实时图像,这样用户就可以设计并拍照或录像。
下面的示例代码演示了如何创建一个Camera预览的基类,这可能被包含在视图布局中。此类实现SurfaceHolder.Callback,
以捕获创建和销毁View的回调事件,这对于分配Camera 预览输入是需要的。
/** A basic Camera preview class */
publicclassCameraPreviewextendsSurfaceViewimplementsSurfaceHolder.Callback{
privateSurfaceHolder mHolder;
privateCamera mCamera;
publicCameraPreview(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);
}
publicvoid 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());
}
}
publicvoid surfaceDestroyed(SurfaceHolder holder){
// empty. Take care of releasing the Camera preview in your activity.
}
publicvoid 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()
方法中任意设定值。
在布局中安置预览(Placing preview in a layout)
一种相机预览类,如前一节中所示的例子中,必须放在一个有其他用来拍照或录像的用户界面控件的Activity的布局中。本节将展示如何构建一个基本的布局和用来预览的Activity。
下面布局代码提供了可用于显示相机预览的一个非常基本的视图(View)。在这个例子中,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>
在大多数设备,摄像头预览的默认方向为横向。此示例布局指定水平(横向)的布局,并且下面的代码可修复应用为横向的方向。为了简化渲染Camera预览,你应该加入以下内容到你的manifest中,以更改应用程序的预览Activity方向为横向。
<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( )再开始预览。
在你相机视图的activity中,添加预览类到FrameLayout
元素,如上面的例子中所示的。它被暂停或关闭时,相机的activity还必须确保能释放Camera,。如下面例子:
publicclassCameraActivityextendsActivity{
privateCamera mCamera;
privateCameraPreview mPreview;
@Override
publicvoid 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 =newCameraPreview(this, mCamera);
FrameLayout preview =(FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
}
注意:上述示例的getCameraInstance()
方法指的是在Accessing cameras例子中显示的方法。
拍照(Capturing pictures)
一旦你建立了一个预览类,并在其中显示它的布局,你就可以用你的应用程序进行拍照。在你的应用程序代码中,您必须为用户界面控件设置监听器以响应用户拍照操作。
为了检索一个图象,使用Camera.takePicture()
方法。此方法需要从Camera接收数据的三个参数。为了接收一个JPEG格式的数据,必须实现Camera.PictureCallback
接口来接收的图像的数据,并将其写入到文件中。下面的代码显示Camera.PictureCallback
接口的基本实现,以保存Camera捕获的图像。
privatePictureCallback mPicture =newPictureCallback(){
@Override
publicvoid 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 =newFileOutputStream(pictureFile);
fos.write(data);
fos.close();
}catch(FileNotFoundException e){
Log.d(TAG,"File not found: "+ e.getMessage());
}catch(IOException e){
Log.d(TAG,"Error accessing file: "+ e.getMessage());
}
}
};
通过调用Camera.takePicture()
方法触发拍照。下面的示例代码显示了如何从按钮的View.OnClickListener调用此方法。
// Add a listener to the Capture button
Button captureButton =(Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
newView.OnClickListener(){
@Override
publicvoid onClick(View v){
// get an image from the camera
mCamera.takePicture(null,null, mPicture);
}
}
);
注意:请记住,当你的应用程序用完Camera以后,要调用Camera.release()方法释放Camera对象!有关如何释放摄像头的信息,请参阅Releasing the camera。
录像(Capturing videos)
采用Android架构的视频拍摄需要仔细管理Camera对象,并且要和MediaRecorder
类保持协调。当用Camera录像时,就必须管理Camera.lock()
和Camera.unlock()的
调用,以允许MediaRecorder
访问Camera的硬件,此外还有Camera.open()
和Camera.release()的
调用。
注:从Android 4.0(API等级14)开始,Camera.lock()
和Camera.unlock()
调用自动为您进行管理。
不像用Camera设备拍照,录像需要一个非常特别的调用顺序。您必须遵守特定的执行顺序,为你的应用程序成功准备录像,详见下文。
- 打开相机 -使用
Camera.open()
获得Camera对象的实例。 - 连接预览 -通过使用相机
Camera.setPreviewDisplay()
连接SurfaceView到Camera准备现场相机图像预览。 - 开始预览 -调用
Camera.startPreview()
开始显示实时相机图像。 - 开始录制视频 -下面的步骤必须完成,才能成功地录制视频:
- 解锁相机 -通过MediaRecorder和调用
Camera.unlock()
解锁相机以供使用。 - 配置MediaRecorder -按照这种顺序调用下面MediaRecorder的方法。欲了解更多信息,请参阅
MediaRecorder
参考文档。setCamera()
-设置Camera用于录像,使用应用程序当前的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
预览布局元素。为Connect Preview使用您所指定的同一个对象。
注意:您必须按此顺序调用这些
MediaRecorder
配置方法,否则你的应用程序将遇到错误,记录将失败。 - 准备MediaRecorder -通过调用MediaRecorder.prepare()用提供的配置设置准备
MediaRecorder
。 - 开始MediaRecorder -通过MediaRecorder.start()开始录像。
- 解锁相机 -通过MediaRecorder和调用
- 停止录制视频 -按顺序调用下面的方法,才能成功地完成视频录制:
- 停止MediaRecorder -通过调用MediaRecorder.stop()停止录像。
- 复位MediaRecorder -通过调用MediaRecorder.reset()删除记录中配置设置。
- 释放MediaRecorder -通过调用
MediaRecorder.release()释放
。MediaRecorder
- 锁定Camera -通过调用使用它
Camera.lock()
锁定相机,使未来MediaRecorder
会话可以使用。从Android 4.0(API等级14)开始,则不需要此调用,除非MediaRecorder.prepare()
调用失败。
- 停止预览 -当你的Activity使用相机完成后,使用Camera.stopPreview()停止预览。
- 释放Camera -通过调用使用它
Camera.release()释放
Camera
,以便让其他应用程序可以继续使用。
注意:它可以使用MediaRecorder
而不用首先创建一个相机预览,并且跳过这一过程中前几个步骤。然而,由于在开始录制之前用户通常更愿意看到一个预览,这个过程不再这里讨论。
提示:如果您的应用程序通常用于录制视频,以开始预览之前,设置setRecordingHint(boolean)为true
。此设置可以帮助减少需要录制的时间。
配置MediaRecorder
要使用MediaRecorder
类来录制视频,则必须按特定的顺序执行配置步骤,然后调用MediaRecorder.prepare()
方法来检查和落实配置。下面的示例代码演示了如何正确配置和准备 MediaRecorder
类来录制视频。
privateboolean prepareVideoRecorder(){
mCamera = getCameraInstance();
mMediaRecorder =newMediaRecorder();
// 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();
returnfalse;
}catch(IOException e){
Log.d(TAG,"IOException preparing MediaRecorder: "+ e.getMessage());
releaseMediaRecorder();
returnfalse;
}
returntrue;
}
在Android 2.2(API等级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(Starting and stopping MediaRecorder)
当使用MediaRecorder
类启动和停止录像,则必须按照特定的顺序执行,如下所示。
- 解锁相机
Camera.unlock()
- 配置
MediaRecorder,
如上图所示的代码示例 - 使用
MediaRecorder.start()开始录制
- 录制视频
- 使用
MediaRecorder.stop()停止录制
使用MediaRecorder.release()
释放MediaRecorder- 使用
Camera.lock()锁定Camera
下面的示例代码演示了使用Camera和 MediaRecorder类
如何连接一个按钮正常启动和停止录制视频。
注:当完成视频录制,不要释放camera,否则预览将被停止。
privateboolean isRecording =false;
// Add a listener to the Capture button
Button captureButton =(Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
newView.OnClickListener(){
@Override
publicvoid 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
}
}
}
}
);注意:相关函数代码在本文档例子中可以找到。
释放相机
相机是一个设备上的应用程序共享的资源。您的应用程序在获得Camera实例以后可以使用Camera,当你的应用程序停止使用Camera时,并且你的应用程序暂停(Activity.onPause()),你必须特别小心地释放Camera对象。如果您的应用程序不能正确地释放Camera,所有后续尝试访问Camera的应用程序,包括自己的应用程序,都将失败,并可能导致您或其他应用程序被关闭。
要释放的一个 Camera实例
对象,就要使用Camera.release()
方法,如在下面的例子中的代码。
publicclassCameraActivityextendsActivity{
privateCamera mCamera;
privateSurfaceView mPreview;
privateMediaRecorder mMediaRecorder;
...
@Override
protectedvoid onPause(){
super.onPause();
releaseMediaRecorder(); // if you are using MediaRecorder, release it first
releaseCamera(); // release the camera immediately on pause event
}
privatevoid releaseMediaRecorder(){
if(mMediaRecorder !=null){
mMediaRecorder.reset(); // clear recorder configuration
mMediaRecorder.release();// release the recorder object
mMediaRecorder =null;
mCamera.lock(); // lock camera for later use
}
}
privatevoid releaseCamera(){
if(mCamera !=null){
mCamera.release(); // release the camera for other applications
mCamera =null;
}
}
}
注意:如果您的应用程序不能正确地释放Camera,所有后续尝试访问Camera的应用程序,包括自己的应用程序,都将失败,并可能导致您或其他应用程序被关闭。
保存媒体文件(Saving Media Files)
被用户创建的媒体文件,如图片和视频,都应该被保存到设备的SD卡中,以节省系统存储空间,并允许用户访问这些文件。在设备上有许多可能的目录位置用来保存媒体文件,不过两个标准的位置用于保存媒体文件,作为开发者你应该考虑这两个标准存储位置:
Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES
) -为了保存图片和视频,这个方法返回的标准,共享和推荐的位置。这个目录是共享的(公共),以便其他应用程序可以很容易地发现,读取,修改和删除已保存在此位置的文件。如果您的应用程序被用户卸载,保存到这个位置的媒体文件不会被删除。为了避免与用户现有的图片和视频的冲突,为了你的应用程序的媒体文件,你应该在此目录中创建一个子目录,如下面的示例代码。这种方法在Android 2.2(API 8级)是可用的,关于Android早期版本API调用情况,请看保存共享文件。Context.getExternalFilesDir
(Environment.DIRECTORY_PICTURES
) -这个方法返回一个标准的位置,用以保存与应用程序相关联的图片和视频。如果您的应用程序被卸载,保存在这个位置的任何文件将被删除。对于保存在这个位置的文件安全是不强制的,其他应用程序可以读取,修改和删除它们。
下面的示例代码演示如何创建一个
或File
,为了一个可能已经被使用的媒体文件,当使用Intent调用设备的Camera,或作为Building a Camera App的一部分时。Uri
publicstaticfinalint MEDIA_TYPE_IMAGE =1;
publicstaticfinalint MEDIA_TYPE_VIDEO =2;
/** Create a file Uri for saving an image or video */
privatestaticUri getOutputMediaFileUri(int type){
returnUri.fromFile(getOutputMediaFile(type));
}
/** Create a File for saving an image or video */
privatestaticFile getOutputMediaFile(int type){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir =newFile(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");
returnnull;
}
}
// Create a media file name
String timeStamp =newSimpleDateFormat("yyyyMMdd_HHmmss").format(newDate());
File mediaFile;
if(type == MEDIA_TYPE_IMAGE){
mediaFile =newFile(mediaStorageDir.getPath()+File.separator +
"IMG_"+ timeStamp +".jpg");
}elseif(type == MEDIA_TYPE_VIDEO){
mediaFile =newFile(mediaStorageDir.getPath()+File.separator +
"VID_"+ timeStamp +".mp4");
}else{
returnnull;
}
return mediaFile;
}
注: Environment.getExternalStoragePublicDirectory()在
Android 2.2的(API等级8级)或更高版本可用的。如果你的设备是Android的早期版本,那就 要使用Environment.getExternalStorageDirectory()
来代替。欲了解更多信息,请参阅Saving Shared Files。
有关保存在Android设备上的文件的详细信息,请参阅Data Storage.。
Camera功能
Android支持的摄像头功能,你可以用你的相机应用程序来控制,比如图片格式 ,闪光模式,对焦设置,等等。本节将列出了常见的Camera功能,并简要介绍如何使用它们。大多数Camera功能可以访问和设置,通过使用Camera.Parameters
对象。但是,在Camera.Parameters中也有几个重要功能,需要比简单设置更多。这些特征包括在以下部分:
有关如何使用功能的一般信息是通过Camera.Parameters控制的
,查看Using camera features 章节。有关通过camera parameters对象如何来控制使用Camera的功能的详细信息,请按照下面链接的功能列表的API参考文档查看详细信息。
表1。
特点 | API级别 | 描述 |
---|---|---|
Face Detection | 14 | 在一个图像内识别人脸,并将其用于聚焦,计量和白平衡 |
Metering Areas | 14 | 在图像内指定一个或更多个区域用于计算白平衡 |
Focus Areas | 14 | 在图像内设定一个或多个区域用于聚焦 |
White Balance Lock | 14 | 停止或启动自动白平衡调整 |
Exposure Lock | 14 | 停止或启动自动曝光调整 |
Video Snapshot | 14 | 在拍摄视频(帧抓取)时,拍照 |
Time Lapse Video | 11 | 与集延迟记录帧数录制时间推移视频 |
Multiple Cameras | 9 | 在设备上支持多个Camera,包括前和后Camera |
Focus Distance | 9 | Camera与对象的距离被称为焦距 |
Zoom | 8 | 图像放大 |
Exposure Compensation | 8 | 增加或减少曝光等级 |
GPS Data | 5 | 在图像中包括地理学上的位置数据 |
White Balance | 5 | 设置白平衡模式,从而影响所拍摄照片中的颜色值 |
Focus Mode | 5 | 如何设置相机聚焦在被拍摄对象上,如自动,固定,微距或无穷大 |
Scene Mode | 5 | 为拍摄特定类型,应用预设模式,如夜间,海滩,雪景,或烛光场景 |
JPEG Quality | 5 | 为JPEG图像设置压缩级别,其增加或减小图像输出文件的质量和大小 |
Flash Mode | 5 | 打开闪光灯,关闭闪光灯,或使用自动设置 |
Color Effects | 5 | 应用颜色特效给所拍摄的图像,例如黑白,棕褐色调或负。 |
Anti-Banding | 5 | 由于JPEG压缩,减少颜色渐变的效果。 |
Picture Format | 1 | 指定图象文件格式 |
Picture Size | 1 | 指定保存图像的像素尺寸 |
注意:由于硬件的不同和软件的实现,这些功能不能在所有的设备上都被支持。有关检查运行在设备上的您的应用程序具备功能的信息,请参阅检查功能的可用性。
检查功能的可用性(Checking feature availability)
当在Android设备上使用Camera功能时,首先要理解的东西是并非所有的相机功能的所有设备都支持。此外,支持特定功能的设备可以支持他们,根据不同级别或不同的选择。因此,你的决策过程的一部分,为您开发的相机应用决定相机的功能要支持到什么程度。做出这个决定后,你应该在Camera应用中设计将要包含代码,用于检查设备是否支持这些功能,如果一个功能不可用,那你的应用程序将失败。
您可以通过获取Camera的参数对象的实例和检查相关的方法,以检查相机功能的可用性,。下面的代码示例展示了如何获取 Camera.Parameters
对象,并检查Camera是否支持自动对焦功能:
// get Camera parameters
Camera.Parametersparams= 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的...()
方法,以确定是否(以及多大程度上)支持此功能。
如果你的应用需要某些正常的拍照功能,你可以在manifest文件中声明这些功能来请求使用它们。当你声明使用特定的拍照功能,如闪光灯和自动对焦,如果你的设备不支持某些功能,在你安装你的应用时,Google Play会限制你应用程序使用这些不支持的功能。对于在manifest中声明Camera功能,请参阅清单 Features Reference。
使用Camera功能
使用Camera.Parameters
对象激活和控制相机的大多数功能。在你第一次获得Camera对象的实例的时候你就获得了这个对象,调用getParameters()
方法,改变返回的参数对象,然后将其设置回Camera对象,如下面的示例代码:
// get Camera parameters
Camera.Parametersparams= mCamera.getParameters();
// set the focus mode
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
// set Camera parameters
mCamera.setParameters(params);
这种技术适用于几乎所有的拍照功能,而且在你获得Camera对象的实例以后大多数参数可以在任何时候改变。变更参数在应用程序的相机预览界面通常立即可见。在软件方面,参数的变化可能需要几帧来实际影响Camera硬件处理新指令,然后发送更新后的图像数据。
重要提示:一些摄像功能不能随意更改。尤其是,改变相机预览的大小和方向,您需要先停止预览,再改变预览大小,然后重新启动预览。从Android 4.0(API等级14)开始,预览方向的改变不再需要重新启动预览。
其他的相机功能需要更多的代码,以便实现,包括:
- 测光和重点领域(Metering and focus areas)
- 人脸检测(Face detection)
- 时间推移视频(Time lapse video)
如何实现这些功能的快速概要在下面的章节中提供。
测光和聚焦区域(Metering and focus areas)
在某些情况下拍摄,自动对焦和测光可能不会产生预期的效果。从Android 4.0开始(API等级14)开始,你的相机应用程序可以提供额外的控制,让您的应用程序或用户指定图像区域,以确定焦点或感光级别设置,并传递这些值给用于拍照或录像的摄像头硬件。
你通过这些方法控制它们的测光和对焦的工作非常类似于其他的拍照功能,在这方面,你通过使用Camera.Parameters
对象中的方法来控制它们。下面的代码演示了为Camera实例如何设置两个测光区域:
// Create an instance of Camera
mCamera = getCameraInstance();
// set Camera parameters
Camera.Parametersparams= mCamera.getParameters();
if(params.getMaxNumMeteringAreas()>0){// check that metering areas are supported
List<Camera.Area> meteringAreas =newArrayList<Camera.Area>();
Rect areaRect1 =newRect(-100,-100,100,100); // specify an area in center of image
meteringAreas.add(newCamera.Area(areaRect1,600));// set weight to 60%
Rect areaRect2 =newRect(800,-1000,1000,-800); // specify an area in upper right of image
meteringAreas.add(newCamera.Area(areaRect2,400));// set weight to 40%
params.setMeteringAreas(meteringAreas);
}
mCamera.setParameters(params);
Camera.Area
对象包含两个数据参数:一个
是为指定摄像机视图之内的区域和一个权重值,它告诉Camera这一区域什么水平的重要性应在测光或集中计算中被给予相机对象。Rect
在Camera.Area
对象中的Rect区域描述一个矩形被映射到一个2000×2000单元格上。坐标-1000,-1000表示的顶部,在摄像机图像的左上角,坐标1000,1000代表的底部,在摄像机图像的右下角,如图所示
图1。红线说明坐标系,用于指定 一个在相机预览内的Camera.Area。蓝框显示位置和Camera区域的形状,其矩形区域值为333,333,667,667。
这个坐标系的边界总是对应于图像中的相机预览可见的外边缘,并且不缩小或与缩放级别扩大。同样,使用Camera.setDisplayOrientation()的
图像预览的旋转 不重新映射坐标系。
人脸检测(Face detection)
对于有人在其中的图片,脸通常是图象最重要的部分,并且在拍照时被用于确定聚焦和白平衡。在Android 4.0(API等级14)框架提供API进行脸部识别,并使用面部识别技术来计算图像设置。
注:当人脸检测功能运行时, setWhiteBalance(String)
, setFocusAreas(List)
和 setMeteringAreas(List)
没有任何效果。
在您的相机应用程序使用脸部识别功能需要几个基本步骤:
- 检查设备支持人脸检测(Check that face detection is supported on the device)
- 创建人脸检测监听器(Create a face detection listener)
- 将人脸检测监听加入到你的Camera对象(Add the face detection listener to your camera object)
- 预览后启动人脸检测(每次预览都要重新启动)(Start face detection after preview (and after every preview restart))
人脸检测功能不是所有的设备都支持。您可以通过调用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() );
}
}
}
创建这个类之后,你再设置它到你的应用程序的 摄像头
的对象,如下面的示例代码:
mCamera.setFaceDetectionListener(new MyFaceDetectionListener());
您的应用程序必须在每次启动时启动人脸检测功能(或重新启动)的摄像头预览。创建启动人脸检测,因此您可以根据需要调用它,如下面的示例代码的方法。
publicvoid startFaceDetection(){
// Try starting Face Detection
Camera.Parametersparams= mCamera.getParameters();
// start face detection only *after* preview has started
if(params.getMaxNumDetectedFaces()>0){
// camera supports face detection, so can start it:
mCamera.startFaceDetection();
}
}
您必须启动人脸检测,在每次启动(或重新启动)的摄像头预览时。如果您使用在Creating a preview class中显示的预览类,添加您startFaceDetection()
方法到在你的预览类中的 surfaceCreated()
和surfaceChanged()
方法中,如下面的示例代码。
publicvoid 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());
}
}
publicvoid 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()
后要调用此方法。不要尝试在你的Camera应用程序的onCreate()方法中调用 startPreview()
,因为此时你的应用程序预览还仍不可见。
http://developer.android.com/guide/topics/media/camera.html#intents