Camera

从今天起,每天翻译一篇开发文档,不是死板翻译,按个人理解,现在翻译不到位以后也会逐渐完善,一切为了学习,哇哈哈。。。今天翻译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

1.setCamera()

2.setAudioSource()-->MediaRecorder.AudioSource.CAMCORDER

3.setVideoSource()-->MediaRecorder.VideoSource.CAMERA.

4.设置视频输出格式和编码:

    2.2+:MediaRecorder.setProfileCamcorderProfile.get()

    2.2-:

setOutputFormat()-->MediaRecorder.OutputFormat.MPEG_4

setAudioEncoder()-->MediaRecorder.AudioEncoder.AMR_NB

setVideoEncoder()-->MediaRecorder.VideoEncoder.MPEG_4_SP

5.setOutputFile()

6.setPreviewDisplay()


c.准备MediaRecorder

MediaRecorder.prepare()


d.开始MediaRcorder

MediaRecorder.start()


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);

还可以配置以下参数


5.停止录制

1.MediaRecorder.stop()

2.MediaRecorder.reset()

3.MediaRecorder.release()

4.Camera.lock()


6.停止预览

Camera.stopPreview()


7.释放摄像头

Camera.release()



开始和停止

  1. Unlock the camera with Camera.unlock()
  2. Configure MediaRecorder as shown in the code example above
  3. Start recording using MediaRecorder.start()
  4. Record the video
  5. Stop recording using MediaRecorder.stop()
  6. Release the media recorder with MediaRecorder.release()
  7. 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对象设置的。


FeatureAPI LevelDescription
Face Detection14人脸识别
Metering Areas14为一张图片制定一个或多个白平衡调节
Focus Areas14为一张图片摄者一个或多个焦点区域
White Balance Lock14自动调节白平衡
Exposure Lock14自动调节曝光
Video Snapshot14捕获图片
Time Lapse Video11延时视频
Multiple Cameras9支持多个摄像头,包含前置和后置谁想头
Focus Distance9摄像头和拍摄物之间的距离
Zoom8设置图片的放大率
ExposureCompensation8增减曝光值
GPS Data5包含或忽略图片的地理位置信息
White Balance5设置平衡模式,影响图片的颜色值
Focus Mode5设置焦点:automatic, fixed, macro or infinity
Scene Mode5预设场景,比如夜晚,海滩,雪景或者烛光晚餐
JPEG Quality5图片质量
Flash Mode5闪光模式
Color Effects5对图片使用颜色
Anti-Banding5减小由于JPEG压缩对颜色渐变的影响
Picture Format1指定图片的格式
Picture Size1指定保存图片的尺寸

注:由于硬件和软件实现的不同,这些功能不是所有的设备都支持。所以在使用的时候需要现检查一下是否支持特定的功能。

比如支持是否自动对焦:

// 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) 不起作用。

第一步:检验是否支持人脸识别功能;

通过getMaxNumDetectedFaces()

第二步:创建人脸识别监听;

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










  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值