内容概要
- 使用系统相机
- 自定义相机(Camera API)
- 拍照
- 录制视频
- 使用相机特性
- Camera2 API介绍
相机Feature和权限
hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
...
使用系统相机
调用系统相机,获取被压缩后的照片(适用于设置头像场景)
static final int REQUEST_IMAGE_CAPTURE = 1;
// 打开系统相机
private void openSystemCameraForPhoto() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
// 获取拍摄结果
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
mImageView.setImageBitmap(imageBitmap);
}
}
调用系统相机,获取原始图片(适用于获取高清图像)
//设置MediaStore.EXTRA_OUTPUT参数(file://类型URI),图片会保存到这个位置
File saveFile = new File('/sdcard/Android/data/${packageName}/temp.jpg');
Uri photoUri = Uri.fromFile(saveFile)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
注意在Android 7.0以后,暴露file:开头的Uri给其外部应用会触发FileUriExposedException
解决方案使用:android.support.v4.content.FileProvider
调用系统相机录制视频
发送Action为MediaStore.ACTION_VIDEO_CAPTURE的Intent,返回intent.getData()获取视频URI
static final int REQUEST_VIDEO_CAPTURE = 1;
private void dispatchTakeVideoIntent() {
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE);
}
}
// 获取录制的视频结果
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
Uri videoUri = intent.getData();
mVideoView.setVideoURI(videoUri);
}
}
使用Camera自定义相机
使用步骤
- 检测并打开相机:Camera.open()
- 创建预览界面:SurfaceView,实现SurfaceHolder.Callback
- 预览相机:mCamera.setPreviewDisplay(mHolder), mCamera.startPreview()
- 获取照片:mCamera.mCamera.takePicture(null, null, jpegCallback)
- 关闭相机:mCamera.release()
打开/关闭Camera
private boolean safeCameraOpen(int id) {
boolean qOpened = false;
try {
releaseCameraAndPreview();
mCamera = Camera.open(id);
qOpened = (mCamera != null);
} catch (Exception e) {
Log.e(getString(R.string.app_name), "failed to open Camera");
e.printStackTrace();
}
return qOpened;
}
private void releaseCameraAndPreview() {
mPreview.setCamera(null);
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
创建相机预览界面
/** 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);
// 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());
}
}
}
拍照并保存图片
private PictureCallback pictureCallbakc = new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// Do save image data
......
}
};
mCamera.takePicture(null, null, pictureCallbakc);
录制视频
基本步骤,调用顺序很重要
- 打开相机:Camera.open()
- 建立预览界面:SurfaceView,Camera.setPreviewDisplay()
- 预览:Camera.startPreview()
- 使用MediaRecorder录制视频
- Unlock the camera,MediaRecorder会调用camera.unlock()
- 配置MediaRecoder
- 设置Camera:recoder.setCamera()
- setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
- setVideoSource(MediaRecorder.VideoSource.CAMERA)
- 设置视频输出格式和编码方式
- API 8及以上使用:recoder.setProfile(CamcorderProfile.get())
- API 8以下必须设置视频输出格式和编码参数
- recoder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
- recoder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
- recoder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP)
- recoder.setOutputFile(fileToSaveVideo)
- recoder.setPreviewDisplay()
- recoder.prepare()
- recoder.start()
- 停止录制视频
- recoder.stop()
- recoder.reset()
- recoder.release()
- Lock the camera:camera.lock()(在API 14及以后不需要调用,MediaRecoder会自动调用,除非MediaRecoder.prepare()方法调用失败了)
- 停止预览,释放camera: Camera.stopPreview(),Camera.release()
相机特性
大部分相机特性可以通过设置Camera.Parameters实现,通过getParameters()方法获取Parameters,更改设置后再设置给Camera对象:Camera.setParameters()
- 测光和调焦(指定图像中特定区域用于进行调焦或光线设置)(API 14)
- 面部识别 (API 14)
- 延时摄影 (API 14)
更多特性,点击查看
检查是否支持某特性
// get Camera parameters
Camera.Parameters params = mCamera.getParameters();
List focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
// Autofocus mode is supported
}
测光和调焦
// 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 meteringAreas = new ArrayList();
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);
}
面部识别
- 检查设备是否支持面部识别;
- 创建一个面部识别的监听器;
- 把面部识别监听器添加给你的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();
}
}
延迟拍摄
延时摄影允许用户把几张图片合成一个几秒或几分钟的视频剪辑。这个功能要使用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
Camera2 API介绍
主要对象介绍
CameraManager
相机管理对象,获取相机列表,获取相机属性,打开相机等操作
CameraManager cameraManager = context.getSystemService(Context.CAMERA_SERVICE)
// 获取相机ID列表
cameraManager.getCameraIdList()
// 获取相机属性
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId)
// 打开相机
cameraManager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
CameraCharacteristics
摄像头属性,相当于原CameraInfo。通过CameraManager获取指定id的摄像头属性
cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
CameraDevice
是Camera2中抽象出来的一个对象,直接与系统硬件摄像头相联系
// 创建预览 Request Builder
CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
// 创建拍照 Rquest Builder
CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
// 创建 CaptureSession
mCameraDevice.createCaptureSession(surfaceOutputList, stateCallback, handlder)
CameraDevice.StateCallback
CameraDevice相机相关的回调:onOpened(相机打开时),onDisconnected(相机断开时),onError(出错时)
CameraCaptureSession
CameraCaptureSession建立了一个和Camera设备的通道,当这个通道建立完成后就可以向Camera发送请求获取图像(预览,拍照等)。
//预览
mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
//拍照
final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
// 设置照片输出surface,mImageReader为照片数据读取工具
captureBuilder.addTarget(mImageReader.getSurface());
// 设置对焦模式
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 发送拍照请求
mCaptureSession.capture(captureBuilder.build(), takePictureCaptureCallback, null);
CameraCaptureSession.StateCallback
创建CameraCaptureSession的回调类,主要方法: onConfigured(创建成功), onConfigureFailed(创建失败)
CaptureRequest 和 CaptureRequest.Builder
一次捕获请求,对相机的操作都通过CaptureRequest完成(比如预览,拍照,录视频),CaptureRequest.Builder 提供Builder模式创建CaptureRequest
CameraCaptureSession.CaptureCallback
捕获回调:onCaptureProgressed(捕获进行中),onCaptureCompleted(捕获完成) ... 等等
ImageReader
ImageReader类允许应用程序直接访问呈现表面的图像数据,getSurface()获取一个表面,在创建CameraCaptureSession时传递给createCaptureSession方法,拍照时通过captureBuilder.addTarget(mImageReader.getSurface());让其获取拍照结果,通过设置监听ImageReader.OnImageAvailableListener,在回调方法onImageAvailable(ImageReader reader)中保存拍照图像
// 创建 ImageReader
mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2);
// 设置图片监听,保存拍照图片
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
// 传递surface给createCaptureSession方法,创建CameraCaptureSession
mCameraDevice.createCaptureSession(Arrays.asList(preViewSurface, mImageReader.getSurface()), stateCallback);
// 拍照时将imageReader的surface作为CaptureRequest捕获输出表面,这个surface必须是在创建CameraCaptureSession时所包含的surface中的一个
final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());
// 这里takePictureCaptureCallback的回调中并没有图片数据,实际是在ImageReader的OnImageAvailableListener回调中保存图片
mCaptureSession.capture(captureBuilder.build(), takePictureCaptureCallback, null);
// OnImageAvailableListener 实现
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireNextImage();
ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
// do save image bytes to disk ...
}
};
Camera2 API 使用流程图
详细API使用请参考Google官方Demo 拍照Demo 录制视频Demo