从今天起,每天翻译一篇开发文档,不是死板翻译,按个人理解,现在翻译不到位以后也会逐渐完善,一切为了学习,哇哈哈。。。今天翻译Camera!
Camera
Android摄像头的功能很丰富,其使用方式有两种,一种是调用系统的摄像头完成图片和视频的拍摄,另一种就是自定义摄像头功能实现。
使用摄像头的时候,先了解几个基本概念:
1.android.hardware.camera2
2.Camera
3.SurfaceView
4.MediaRecorder
5.Intent
MediaStore.ACTION_IMAGE_CAPTURE
/MediaStore.ACTION_VIDEO_CAPTURE
在Manifest中的声明
1.相机权限声明:
<!--如果是使用系统的相机拍照,可以不用声明此权限-->
<uses-permission android:name="android.permission.CAMERA" />
2.功能声明:
<!--只有带有摄像头的Android设备才能安装-->
<uses-feature android:name="android.hardware.camera" />
<!--不要求只有待有摄像头的Android设备才能安装-->
<uses-feature android:name="android.hardware.camera" android:required="false" />
3.存储权限声明:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
4.录制视频的权限声明:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
5.位置权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
用系统的相机拍照或录制视频的步骤:
1.创建Intent
2.startActivityForResult()
3.onActivityResult()
拍照的Intent:
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);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
视频的Intent:
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);
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // 设置视频质量,从0到1由低到高,还可以设置时长(<code><a target=_blank href="http://developer.android.com/reference/android/provider/MediaStore.html#EXTRA_DURATION_LIMIT">MediaStore.EXTRA_DURATION_LIMIT</a></code>)和大小(<code><a target=_blank href="http://developer.android.com/reference/android/provider/MediaStore.html#EXTRA_SIZE_LIMIT">MediaStore.EXTRA_SIZE_LIMIT</a></code>)
startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
}
接收Intent:
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中指定的图片保存路径fileUri
Toast.makeText(this, "Image saved to:\n" + data.getData(), Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
// 用户取消拍摄图像后
} else {
// 图像捕获失败
}
}
if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
// 返回在Intent中指定的视频保存路径fileUri
Toast.makeText(this, "Video saved to:\n" + data.getData(), Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
// 用户取消录制后
} else {
// 视屏录制失败
}
}
}
以上用到的:getOutputMediaFile(int type)
private static File getOutputMediaFile(int type){
// 检查SD卡
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp");
// 这个地方是个好位置,如果你想给别的应用程序共享图片和视频,或者你希望你的应用在卸载之后忍让能够使用图片和视频,不太明白这是什么意思,求指点
// 是在这里把照片和视频加到设备的媒体库中吗?
// 如果目录不存在就创建
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;
}
自定义相机的介绍:
第一步,先检查是否支持自定义相机
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// 设备有摄像头
return true;
} else {
// 设备没有摄像头
return false;
}
}
可以通过Camera.getParameters()或者Camera.Parameters获取摄像头的许多功能特性和参数
还可以通过Camera.getCameraInfo()获知摄像头的前、后属性和图片的方向
第二步,创建预览视图
/** 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;
mHolder = getHolder();
mHolder.addCallback(this);
// 不建议使用,但在3.0之前要求调用
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// 记得在onDestory中释放preview
}
<pre name="code" class="java"> // 注意preview的改变或者旋转,如果要改变大小或者格式,确保preview先stop
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null){
return;
}
// 改变之前先stop
try {
mCamera.stopPreview();
} catch (Exception e){
}
//设置新的大小,方向和格式
// 重新打开预览
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
第三步,预览布局
<?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>
public class CameraActivity extends Activity {
private Camera mCamera;
private CameraPreview mPreview;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
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);
}
}
/** 以一种安全的方式获取相机实例,捕获异常信息为了防止相机获取不成功的情况 */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open();
}
catch (Exception e){
// 摄像头不可用(正在使用/没有摄像头)
}
return c; // 如果摄像头不可用,则返回null
}
第四步,拍照监听
// 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) {
mCamera.takePicture(null, null, mPicture);
}
}
);
第五步,捕获和保存
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.realse();
注:摄像头是共享的,所以在使用是必须小心的管理它,在使用完之后也要记得释放,否则下次调用时将会报错。
视频捕获
视频拍摄要求小心的管理Camera对象和MediaRecorder类,除了Camera.open()和Camera.close(),还要管理Camera.lock()和Camera.unlock()
1.打开摄像头
Camera.open()
2.连接预览
Camera.setPreviewDisplay()
3.打开预览
Camera.startPreview()
4.开始录制
a.解锁相机
Camera.unlock()
b.配置MediaRecorder
2.setAudioSource()
-->MediaRecorder.AudioSource.CAMCORDER
3.setVideoSource()
-->MediaRecorder.VideoSource.CAMERA
.
4.设置视频输出格式和编码:
2.2+:MediaRecorder.setProfile
,CamcorderProfile.get()
2.2-:
setOutputFormat()
-->MediaRecorder.OutputFormat.MPEG_4
setAudioEncoder()
-->MediaRecorder.AudioEncoder.AMR_NB
setVideoEncoder()
-->MediaRecorder.VideoEncoder.MPEG_4_SP
c.准备MediaRecorder
d.开始MediaRcorder
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;
}
在2.2之前
// 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);
还可以配置以下参数
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
5.停止录制
6.停止预览
7.释放摄像头
开始和停止
- Unlock the camera with
Camera.unlock()
- Configure
MediaRecorder
as shown in the code example above - Start recording using
MediaRecorder.start()
- Record the video
- Stop recording using
MediaRecorder.stop()
- Release the media recorder with
MediaRecorder.release()
- Lock the camera using
Camera.lock()
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
}
}
}
}
);
释放摄像头
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;
}
}
}
保存
两个标准路径保存文件:
Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES
):共享路径,当应用程序卸载后不会被删除
Context.getExternalFilesDir
(Environment.DIRECTORY_PICTURES
):当应用程序卸载后会被删除
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;
private static Uri getOutputMediaFileUri(int type){
return Uri.fromFile(getOutputMediaFile(type));
}
private static File getOutputMediaFile(int type){
//应该先检查SD卡的状态
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp");
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
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;
}
摄像头的功能
Android支持许多你可以控制的摄像头功能,比如图片格式,闪光模式,焦点设置等等。以下列举通用的摄像头功能和描述怎么使用它们。许多功能是通过Camera.Parameters
对象设置的。
Feature | API Level | Description |
---|---|---|
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 | 支持多个摄像头,包含前置和后置谁想头 |
Focus Distance | 9 | 摄像头和拍摄物之间的距离 |
Zoom | 8 | 设置图片的放大率 |
ExposureCompensation | 8 | 增减曝光值 |
GPS Data | 5 | 包含或忽略图片的地理位置信息 |
White Balance | 5 | 设置平衡模式,影响图片的颜色值 |
Focus Mode | 5 | 设置焦点:automatic, fixed, macro or infinity |
Scene Mode | 5 | 预设场景,比如夜晚,海滩,雪景或者烛光晚餐 |
JPEG Quality | 5 | 图片质量 |
Flash Mode | 5 | 闪光模式 |
Color Effects | 5 | 对图片使用颜色 |
Anti-Banding | 5 | 减小由于JPEG压缩对颜色渐变的影响 |
Picture Format | 1 | 指定图片的格式 |
Picture Size | 1 | 指定保存图片的尺寸 |
注:由于硬件和软件实现的不同,这些功能不是所有的设备都支持。所以在使用的时候需要现检查一下是否支持特定的功能。
比如支持是否自动对焦:
// 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...(),isSupported...(),或者getMax...()方法去判断某些特定的摄像头功能。
最后,可以在Manifest中声明指定的摄像头相关功能,这样Google Play会限制没有该功能的设备不予下载使用应用程序。
摄像头功能的使用:
// 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);
大多数摄像头的功能都可以通过如上方式设置,但是有一些功能不能被随便设置,比如拍摄图片大小和方向,必须要先停止预览,改变之后再重新预览,4.0之后,想要改变方向可以不用重启预览。
还有一些功能需要编写更多的代码才能实现,比如:
- Metering and focus areas
- Face detection
- Time lapse video
测光和调距:
在一些摄影场景中,自定对焦和测光不能满足预期的效果,从4.0(api14)开始,允许自己定义这些功能属性:
// 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);
人脸识别功能:
人脸识别开启式,setWhiteBalance(String)
,setFocusAreas(List)
和setMeteringAreas(List)
不起作用。
第一步:检验是否支持人脸识别功能;
第二步:创建人脸识别监听;
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();
}
}
每次开始预览的时候都必须打开人脸识别,在surfaceCreated()和surfaceChanged中都要调用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());
}
}
注:不要相机界面Activity的onCreate()中调用人脸识别方法,这个时候的preview还不可用,要在starPreview()之后调用人脸识别。
延时摄影:
延时摄运行用户把几张图片合成一个几秒或者几分钟的视频剪辑。这个功能需要使用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