Android Camera详解
来源:互联网 发布:mac模糊搜索文件 编辑:程序博客网 时间:2024/06/05 13:31
本文译自官方文档:https://developer.android.com/guide/topics/media/camera.html
Android框架层包含了对多种相机和相机特性的支持,可以让你在你的应用中拍照或录像。本文档主要讨论如何快速、简单的进行拍照和录像,同时也对如何开发复杂一些的相机应用做了简要介绍。
1 基础
Android框架层支持通过android.hardware.camera2
API 或 camera Intent来拍照和录像,以下是相关的类:
android.hardware.camera2
这个包提供了控制相机设备的主要API。
Camera
已过时的控制相机设备的API。
SurfaceView
这个类用来向用户展示实时的相机预览(live camera preview)。
MediaRecorder
这个类用来录像。
Intent
不需要直接操作相机设备,通过MediaStore.ACTION_IMAGE_CAPTURE
和MediaStore.ACTION_VIDEO_CAPTURE
这两个Intent就可以快速地进行拍照和录像。
2 清单文件声明
在开始使用相机API进行开发之前,首先要确保你的清单文件中已经声明了相应的权限和特性。
相机权限 —— 如果你的app要直接使用相机设备,则必须声明此权限。
<uses-permission android:name="android.permission.CAMERA" />
注意:如果是通过Intent来间接使用相机,则不需要声明此权限。
相机特性(Camera Features)
<uses-feature android:name="android.hardware.camera" />
如果在清单文件中声明了相机特性,那么Google Play会阻止你的app被安装在不支持相机的设备上。
存储权限 —— 如果你的app将相片和视频存储在外部存储设备(SD卡),则必须声明此权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
录音权限 —— 如果需要在录像的同时录音,则必须声明录音权限。
<uses-permission android:name="android.permission.RECORD_AUDIO" />
获取位置信息权限 —— 如果想要给拍摄的照片加上位置信息,则必须声明此权限。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3 通过Intent使用已有的相机app来拍照和录像
在你的app中进行拍照和录像的一个简单方法是:通过Intent来调起一个已有的相机应用。有如下几步:
- 创建Intent
MediaStore.ACTION_IMAGE_CAPTURE
:通过已有的相机应用拍照。MediaStore.ACTION_VIDEO_CAPTURE
:通过已有的相机应用录像。
- 发送Intent:调用
startActivityForResult()
。 - 接收拍照或录像的结果:在
onActivityResult()
方法中接收拍照或录像的结果。
(1) 拍照intent
拍照Intent可以包含如下额外信息:
MediaStore.EXTRA_OUTPUT
:设置照片保存的路径(包含文件名),值为一个Uri对象。此项设置是可选的,但是强烈建议进行设置。如果不设置,则照片会以默认的名字保存在默认路径,通过调用onActivityResult()
方法中参数intent的getData()
方法,可以获得照片的保存路径(uri)。
以下为示例代码,其中getOutputMediaFileUri(...)
方法的实现见后面的存储媒体文件部分:
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;private Uri fileUri;@Overridepublic 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);}
(2) 录像intent
录像Intent可以包含如下额外信息:
MediaStore.EXTRA_OUTPUT
:同上,设置视频保存的路径(包含文件名),不再赘述。MediaStore.EXTRA_VIDEO_QUALITY
:设置视频的质量。取值从0到1,1代表最高的视频质量(和最大的文件尺寸)。MediaStore.EXTRA_DURATION_LIMIT
:限制视频的时长,以秒为单位。MediaStore.EXTRA_SIZE_LIMIT
:限制视频文件的大小,以字节为单位。
以下为示例代码,其中getOutputMediaFileUri(...)
方法的实现见后面的存储媒体文件部分:
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;private Uri fileUri;@Overridepublic 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);}
(3) 接收拍照或录像的结果
为了接收拍照或录像的结果,你需要覆写Acitivity的onActivityResult(...)
方法,如下例所示:
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;@Overrideprotected 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 } }}
4 直接操作相机设备来拍照和录像
如果你想要定义自己的拍照和录像界面,或者需要更高级一些的功能,那么Intent方式就不能满足要求了——你需要直接操作相机设备来达到目的。
注意:下面的讲述以过时的
Camera
中的API为例。但如果你是开发一个新的app,不考虑向下兼容性,那么建议使用android.hardware.camera2
中的新API(仅支持API level 21以上,即Android 5.0以上的设备)。
步骤:
- 检测和访问相机:使用代码检测设备上是否有相机设备,如果有则打开相机设备。
- 创建预览视图:创建一个类用于展示相机预览画面,它需要继承
SurfaceView
类并实现SurfaceHolder
接口。 - 创建布局:创建一个布局以容纳预览视图以及用户操作界面。
- 设置画面采集的开关:为用户操作界面中的按钮设置监听,当点击按钮时就开始或停止采集画面。
- 画面采集与保存:采集静态图像或视频,并保存。
- 释放相机资源:在使用完相机资源之后,必须将其释放。
注意:在使用完相机资源之后一定要记得使用
Camera.release()
将其释放,否则之后的任何打开相机操作都将失败(无论是其他app还是当前app),这可能会造成错误和app闪退。
侦测相机设备
在使用相机之前,首先要检测当前设备上是否有相机设备:
/** 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 Level 9)以上,你可以使用Camera.getNumberOfCameras()
来获取当前设备上的相机数量。
访问相机
通过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}
在Android 2.3(API Level 9)以上的设备上,你可以使用Camera.open(int id)
来获得指定的相机。
获取相机的更多信息
在获得了相机实例之后,你可以通过调用其Camera.getParameters()
方法来获得此相机的更多信息。在API Level 9及以上的设备上,还可以使用Camera.getCameraInfo()
来查看此相机是前置相机还是后置相机,以及相机画面的方向。
创建预览视图
下面的代码说明了如何创建一个类用于展示相机预览画面。这个类是SurfaceView
的子类,因而可以实时显示来自相机的画面数据;实现了SurfaceHolder.Callback
接口,因而可以监听surface的创建和销毁。
/** 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()); } }}
如果你想使用setPreviewSize(...)
设置相机预览数据的宽高尺寸,那么就按照上面注释中的说明,在surfaceChanged(...)
方法中进行设置。但是要注意,相机预览的宽高尺寸必须是getSupportedPreviewSizes(...)
方法的返回值中有的,不能随意设置。
将预览视图放到布局中
下面的示例代码创建了一个简单的Acitivity布局,用来容纳预览视图。其中的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>
在绝大多数设备上,相机预览画面都是横屏(landscape)的,因此为了简单一点,我们也将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 Level 8)及以上,可以使用
setDisplayOrientation()
来改变相机预览画面的方向。但是,如果相机预览正在进行的话,需要先停止预览(调用Camera.stopPreview()
),然后改变相机预览画面的方向,最后再重新开始相机预览(调用Camera.startPreview()
)。
下面的代码展示了如何将预览视图()添加到Activity的布局中:
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()
来拍摄一张照片:
// Add a listener to the Capture buttonButton 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
是一个回调,其代码如下:
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()); } }};
录像
(1) 概述
录像使用的是MediaRecorder
。除了Camera.open()
和Camera.release()
之外,你还需要适时的调用Camera.lock()
和Camera.unlock()
,这样MediaRecorder
才能成功地访问到相机设备。
注意:从Android 4.0(API level 14)开始,
Camera.lock()
和Camera.unlock()
会被自动调用,不再需要你去手动调用。
录像有严格的步骤和顺序,如下所示:
1 打开相机
调用Camera.open()
来获得一个相机实例。
2 连接到预览视图
调用Camera.setPreviewDisplay()
以使Camera和SurfaceView建立关联。
3 开始相机预览
调用Camera.startPreview()
。
4 开始录像
下面的步骤必须严格按照顺来执行:
a.解锁相机:调用Camera.unlock()
。
b.配置MediaRecorder:按顺序调用MediaRecorder的下列方法。
1. setCamera()2. setAudioSource():设置音频信号源, 使用MediaRecorder.AudioSource.CAMCORDER. 3. setVideoSource():设置视频信号源,使用MediaRecorder.VideoSource.CAMERA.4. 设置视频输出格式与编码: 从Android 2.2(API Level 8)开始,直接调用MediaRecorder.setProfile(CamcorderProfile.get(...))即可。 对于Android 2.2以前的版本,调用setOutputFormat()、setAudioEncoder()和setVideoEncoder()来进行设置。5. setOutputFile():设置输出文件6. setPreviewDisplay():注意这是MediaRecorder的setPreviewDisplay()方法。注意:必须按顺序调用以上方法,否则可能会出错。
c.准备MediaRecorder:调用MediaRecorder.prepare()
使上面的配置生效。
d.启动MediaRecorder:调用MediaRecorder.start()
。
5 停止录像
按照顺序调用如下方法:
a.停止MediaRecorder:调用MediaRecorder.stop()
。
b.重置MediaRecorder:调用MediaRecorder.reset()
,此操作是可选的。
c.释放MediaRecorder:调用MediaRecorder.release()
。
d.锁定相机:调用Camera.lock()
。
6 停止相机预览
调用Camera.stopPreview()
。
7 释放相机资源
调用Camera.release()
。
(2) 配置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;}
除了上面提到的之外,MediaRecorder还有以下设置录像参数的方法:
setVideoEncodingBitRate()setVideoSize()setVideoFrameRate()setAudioEncodingBitRate()setAudioChannels()setAudioSamplingRate()
(3) 启动和停止MediaRecorder
private boolean isRecording = false;// Add a listener to the Capture buttonButton 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 releaseMediaRecorder(); // inform user } } } });
释放相机资源
相机是被设备上众多app共享的资源,因此当不使用的时候一定要记得调用Camera.release()
将其释放,否则之后的任何打开相机操作都将失败(无论是其他app还是当前app),这可能会造成错误和app闪退。
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; } }}
5 存储媒体文件
为了节省内部存储空间,诸如图片、视频之类的媒体文件最好是存储在手机的外部存储(sd卡)中。虽然存储在sd卡的任何位置都是可以的,但是建议使用如下两种标准的存储目录:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
——这个方法会返回一个用于存储图片和视频的、标准的、共享的目录。所谓共享就是说,其他app可以轻易的发现、读取、修改和删除此目录下的文件。当你的app被用户卸载时,此目录下的文件并不会被删除。为了避免混乱,建议在此目录下创建一个子目录来存放你app中的图片和视频。Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
——此目录与你的app相关联,当你的app被用户卸载时,此目录中的文件也会被删除。此外,其他app也可以读取、修改和删除此目录下的文件。
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;}
6 相机特性(Camera Features)
Android支持众多的相机特性,比如图片格式、闪光灯模式、对焦设置等等。下面我们将列出常用的相机特性,并简要的讨论如何使用它们。大部分相机特性都可以通过Camera.Parameters
来设置和使用,但也有几个重要的相机特性需要更多的操作,它们是:测光和对焦区域、面部侦测、时间推移视频。
关于如何使用由Camera.Parameters
控制的相机特性,参考下面的使用相机特性
章节。
表格1. 常用的相机特性(按API Level排序)
注意,以上相机特性并非在所有的设备上都可用(取决于硬件差异和软件实现)。
检查相机特性是否可用
首先需要明白的是,并非所有设备都支持所有的相机特性。此外,不同设备对某一特性支持的程度也不同。
下面代码以对焦为例,展示了如何获取Camera.Parameters
对象,以及如何确定当前设备是否支持自动对焦:
// get Camera parametersCamera.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...()
之类的方法用来检查当前设备是否支持某一相机特性。
你可以在清单文件中声明某一相机特性,比如闪光灯、自动对焦等,这样Google Play就会阻止你的app被安装到不支持这些特性的设备上。
使用相机特性
以自动对焦为例:
// get Camera parametersCamera.Parameters params = mCamera.getParameters();// set the focus modeparams.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);// set Camera parametersmCamera.setParameters(params);
上面的做法适用于几乎所有相机特性。此外,大部分的参数都可以在任何时候被改变。
测光和对焦区域(Metering and focus areas)
有时候自动对焦和测光并不能达到你想要的效果,这时不妨尝试手动指定测光和对焦区域。
下面代码展示了如何为相机指定两个测光区域:
// Create an instance of CameramCamera = getCameraInstance();// set Camera parametersCamera.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*2000的单元网格中的矩形。坐标-1000,-1000
代表相机图像的左上角,坐标1000,1000
代表相机图像的右下角,如下图所示。
坐标系的边界始终与相机预览图像的边界对应,不会因为变焦而缩小或者扩大。同样,使用
Camera.setDisplayOrientation()
对相机预览图像进行旋转也不会影响此坐标系。
面部侦测
对于含有人像的照片,人的面部通常是最为重要的部分,应该被作为对焦和白平衡的依据。 Android 4.0(API Level 14)提供了对面部侦测的支持。
注意:当面部侦测运行时,
setWhiteBalance(String)
,setFocusAreas(List)
和setMeteringAreas(List)
都无效。
要在app中使用面部侦测需要遵循以下步骤:
- 检测当前设备是否支持面部侦测
- 为Camera对象设置面部侦测的监听器
- 在相机预览开始之后启动面部侦测
并非所有设备都支持面部侦测,你可以通过调用getMaxNumDetectedFaces()
来确定当前设备是否支持面部侦测。
为了在侦测到面部时得到通知,你需要创建一个Camera.FaceDetectionListener
对象,并将它设置给Camera对象:
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());
检测设备是否支持面部侦测并启动面部侦测:
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(); }}
在相机预览开始之后启动面部侦测,如下面代码所示:
...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()); }}...
时间推移视频(Time lapse video)
时间推移视频,其实就是延时摄影视频,即每隔一段时间拍摄一张照片,然后用这些照片生成一个视频。
时间推移视频也是通过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 numbermMediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds
配置完MediaRecorder之后,你就可以开始录制一个时间推移视频了,就像录制一个普通视频那样。
- Android Camera 使用详解
- Android Camera 使用详解
- Android Camera 使用详解
- Android Camera使用详解
- Android Camera、Camera2详解
- Android Camera API详解
- Android Camera详解
- Android Camera 相机开发详解
- android中 camera 拍照应用详解
- CameraSurfaceView——Android Camera使用详解
- com.android.camera.Camera
- Android camera
- Android Camera
- android camera
- android camera
- Android Camera
- Android Camera
- Android Camera
- svn 导出项目类和方法有红色下划线
- ResourceExhaustedError解决方法
- 十三 iOS之 音量振动条
- erlang高性能网络库esockd的编译和使用(五)-热更新
- QT之qss教程-QToolBox
- Android Camera详解
- android studo 3.0 搜狗输入法问题 终结者
- Android studio 解决各种错误
- linux 哲学家进餐问题 c++
- 优化代码建议
- HBase之HRegionServer处理put请求
- 技术分享连载(四十八)
- Opencv cvCircle()函数
- 服务器常见错误代码500、501、502、503、504、505