Android相机详解

内容概要

  • 使用系统相机
  • 自定义相机(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);

录制视频

基本步骤,调用顺序很重要

  1. 打开相机:Camera.open()
  2. 建立预览界面:SurfaceView,Camera.setPreviewDisplay()
  3. 预览:Camera.startPreview()
  4. 使用MediaRecorder录制视频
    • Unlock the camera,MediaRecorder会调用camera.unlock()
    • 配置MediaRecoder
      1. 设置Camera:recoder.setCamera()
      2. setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
      3. setVideoSource(MediaRecorder.VideoSource.CAMERA)
      4. 设置视频输出格式和编码方式
        • API 8及以上使用:recoder.setProfile(CamcorderProfile.get())
        • API 8以下必须设置视频输出格式和编码参数
          1. recoder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
          2. recoder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
          3. recoder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP)
      5. recoder.setOutputFile(fileToSaveVideo)
      6. recoder.setPreviewDisplay()
    • recoder.prepare()
    • recoder.start()
  5. 停止录制视频
    1. recoder.stop()
    2. recoder.reset()
    3. recoder.release()
    4. Lock the camera:camera.lock()(在API 14及以后不需要调用,MediaRecoder会自动调用,除非MediaRecoder.prepare()方法调用失败了)
  6. 停止预览,释放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);
}

面部识别

  1. 检查设备是否支持面部识别;
  2. 创建一个面部识别的监听器;
  3. 把面部识别监听器添加给你的Camera对象;
  4. 在预览开始之后(并且在每次重启预览窗口之后)都要启动面部识别。
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 使用流程图

Camera2 API流程图

详细API使用请参考Google官方Demo  拍照Demo   录制视频Demo

你可能感兴趣的:(Android相机详解)