CameraSurfaceView——Android Camera使用详解

来源:互联网 发布:剑三捏脸数据不合法 编辑:程序博客网 时间:2024/06/08 09:38

本篇文章将围绕自定义控件——CameraSurfaceView来阐述Android Camera的使用详解,这里的Camera不包含Camera2,因为Camera2只适应5.0+的安卓系统,而目前市面上5.0以下的安卓设备还是有相当大的一部分的,所以当前的主流还是使用Camera来开发相机。首先先介绍下CameraSurfaceView,这是一个集拍照,录像,闪光灯,摄像头切换,前后台切换,自适应横竖屏于一体的SurfaceView,而我们平常使用Camera无非也就这几样功能(当然还有例如直播所会使用的Camera预览帧编解码,这个控件暂时没有,后期会补上来),

CameraSurfaceView使用方法也很简单,如下:

1.赋予程序必要的权限;2.布局文件中添加<com.yph.camerasurfaceview.CameraSurfaceView    android:id="@+id/cameraSurfaceView"    android:layout_width="match_parent"    android:layout_height="match_parent" />或者 new CameraSurfaceView(context),即可。3.拍照:cameraSurfaceView.capture();  录像:cameraSurfaceView.startRecord();//开始录制        cameraSurfaceView.stopRecord(); //结束录制        //设置录制时长为10秒视频        cameraSurfaceView.startRecord(10000, new MediaRecorder.OnInfoListener() {            @Override            public void onInfo(MediaRecorder mr, int what, int extra) {                cameraSurfaceView.stopRecord();            }        });  闪光灯:cameraSurfaceView.switchLight(boolean open);  前后摄像头的切换:cameraSurfaceView.setDefaultCamera(boolean backCamera);  前后台切换:cameraSurfaceView.setRunBack(boolean b)4.释放Camera:  @Override  protected void onDestroy() {      super.onDestroy();      cameraSurfaceView.closeCamera();  }


主体类如下(已经包含了上述所有功能代码的实现):


public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback, View.OnClickListener {    protected static final int[] VIDEO_320 = {320, 240};    protected static final int[] VIDEO_480 = {640, 480};    protected static final int[] VIDEO_720 = {1280, 720};    protected static final int[] VIDEO_1080 = {1920, 1080};    private int screenOritation = Configuration.ORIENTATION_PORTRAIT;    private boolean mOpenBackCamera = true;    private SurfaceHolder mSurfaceHolder;    private SurfaceTexture mSurfaceTexture;    private boolean mRunInBackground = false;    boolean isAttachedWindow = false;    private Camera mCamera;    private Camera.Parameters mParam;    private byte[] previewBuffer;    private int mCameraId;    protected int previewformat = ImageFormat.NV21;    Context context;    public CameraSurfaceView(Context context) {        super(context);        init(context);    }    public CameraSurfaceView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    public CameraSurfaceView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init(context);    }    private void init(Context context) {        this.context = context;        cameraState = CameraState.START;        if (cameraStateListener != null) {            cameraStateListener.onCameraStateChange(cameraState);        }        openCamera();        if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {            screenOritation = Configuration.ORIENTATION_LANDSCAPE;        }        mSurfaceHolder = getHolder();        mSurfaceHolder.addCallback(this);        mSurfaceTexture = new SurfaceTexture(10);        setOnClickListener(this);        post(new Runnable() {            @Override            public void run() {                if(!isAttachedWindow){                    mRunInBackground = true;                    startPreview();                }            }        });    }    @Override    protected void onAttachedToWindow() {        super.onAttachedToWindow();        isAttachedWindow = true;    }    private void openCamera() {        if (mOpenBackCamera) {            mCameraId = findCamera(false);        } else {            mCameraId = findCamera(true);        }        if (mCameraId == -1) {            mCameraId = 0;        }        try {            mCamera = Camera.open(mCameraId);        } catch (Exception ee) {            mCamera = null;            cameraState = CameraState.ERROR;            if (cameraStateListener != null) {                cameraStateListener.onCameraStateChange(cameraState);            }        }        if (mCamera == null) {            Toast.makeText(context, "打开摄像头失败", Toast.LENGTH_SHORT).show();            return;        }    }    private int findCamera(boolean front) {        int cameraCount;        try {            Camera.CameraInfo cameraInfo = new Camera.CameraInfo();            cameraCount = Camera.getNumberOfCameras();            for (int camIdx = 0; camIdx < cameraCount; camIdx++) {                Camera.getCameraInfo(camIdx, cameraInfo);                int facing = front ? 1 : 0;                if (cameraInfo.facing == facing) {                    return camIdx;                }            }        } catch (Exception e) {            e.printStackTrace();        }        return -1;    }    public boolean setDefaultCamera(boolean backCamera) {        if (mOpenBackCamera == backCamera) return false;        if (isRecording) {            Toast.makeText(context, "请先结束录像", Toast.LENGTH_SHORT).show();            return false;        }        mOpenBackCamera = backCamera;        if (mCamera != null) {            closeCamera();            openCamera();            startPreview();        }        return true;    }    public void closeCamera() {        stopRecord();        stopPreview();        releaseCamera();    }    private void releaseCamera() {        try {            if (mCamera != null) {                mCamera.setPreviewCallback(null);                mCamera.setPreviewCallbackWithBuffer(null);                mCamera.stopPreview();                mCamera.release();                mCamera = null;            }        } catch (Exception ee) {        }    }    private boolean isSupportCameraLight() {        boolean mIsSupportCameraLight = false;        try {            if (mCamera != null) {                Camera.Parameters parameter = mCamera.getParameters();                Object a = parameter.getSupportedFlashModes();                if (a == null) {                    mIsSupportCameraLight = false;                } else {                    mIsSupportCameraLight = true;                }            }        } catch (Exception e) {            mIsSupportCameraLight = false;            e.printStackTrace();        }        return mIsSupportCameraLight;    }    private Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {        public synchronized void onPreviewFrame(byte[] data, Camera camera) {            if (data == null) {                releaseCamera();                return;            }            //you can code media here            if (cameraState != CameraState.PREVIEW) {                cameraState = CameraState.PREVIEW;                if (cameraStateListener != null) {                    cameraStateListener.onCameraStateChange(cameraState);                }            }            mCamera.addCallbackBuffer(previewBuffer);        }    };    //设置Camera各项参数    private void startPreview() {        if (mCamera == null) return;        try {            mParam = mCamera.getParameters();            mParam.setPreviewFormat(previewformat);            mParam.setRotation(0);            Camera.Size previewSize = CamParaUtil.getSize(mParam.getSupportedPreviewSizes(), 1000,                    mCamera.new Size(VIDEO_720[0], VIDEO_720[1]));            mParam.setPreviewSize(previewSize.width, previewSize.height);            int yuv_buffersize = previewSize.width * previewSize.height * ImageFormat.getBitsPerPixel(previewformat) / 8;            previewBuffer = new byte[yuv_buffersize];            Camera.Size pictureSize = CamParaUtil.getSize(mParam.getSupportedPictureSizes(), 1500,                    mCamera.new Size(VIDEO_1080[0], VIDEO_1080[1]));            mParam.setPictureSize(pictureSize.width, pictureSize.height);            if (CamParaUtil.isSupportedFormats(mParam.getSupportedPictureFormats(), ImageFormat.JPEG)) {                mParam.setPictureFormat(ImageFormat.JPEG);                mParam.setJpegQuality(100);            }            if (CamParaUtil.isSupportedFocusMode(mParam.getSupportedFocusModes(), FOCUS_MODE_AUTO)) {                mParam.setFocusMode(FOCUS_MODE_AUTO);            }            if (screenOritation != Configuration.ORIENTATION_LANDSCAPE) {                mParam.set("orientation", "portrait");                mCamera.setDisplayOrientation(90);            } else {                mParam.set("orientation", "landscape");                mCamera.setDisplayOrientation(0);            }            if (mRunInBackground) {                mCamera.setPreviewTexture(mSurfaceTexture);                mCamera.addCallbackBuffer(previewBuffer);//                mCamera.setPreviewCallbackWithBuffer(previewCallback);//设置摄像头预览帧回调            } else {                mCamera.setPreviewDisplay(mSurfaceHolder);//                mCamera.setPreviewCallback(previewCallback);//设置摄像头预览帧回调            }            mCamera.setParameters(mParam);            mCamera.startPreview();            if (cameraState != CameraState.START) {                cameraState = CameraState.START;                if (cameraStateListener != null) {                    cameraStateListener.onCameraStateChange(cameraState);                }            }        } catch (Exception e) {            releaseCamera();            return;        }        try {            String mode = mCamera.getParameters().getFocusMode();            if (("auto".equals(mode)) || ("macro".equals(mode))) {                mCamera.autoFocus(null);            }        } catch (Exception e) {        }    }    private void stopPreview() {        if (mCamera == null) return;        try {            if (mRunInBackground) {                mCamera.setPreviewCallbackWithBuffer(null);                mCamera.stopPreview();            } else {                mCamera.setPreviewCallback(null);                mCamera.stopPreview();            }            if (cameraState != CameraState.STOP) {                cameraState = CameraState.STOP;                if (cameraStateListener != null) {                    cameraStateListener.onCameraStateChange(cameraState);                }            }        } catch (Exception ee) {        }    }    @Override    public void onClick(View v) {        if (mCamera != null) {            mCamera.autoFocus(null);        }    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        stopPreview();        startPreview();    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        stopPreview();        if (mRunInBackground)            startPreview();    }    protected CameraState cameraState;    private CameraStateListener cameraStateListener;    public enum CameraState {        START, PREVIEW, STOP, ERROR    }    public void setOnCameraStateListener(CameraStateListener listener) {        this.cameraStateListener = listener;    }    public interface CameraStateListener {        void onCameraStateChange(CameraState paramCameraState);    }    /**     * ___________________________________前/后台运行______________________________________     **/    public void setRunBack(boolean b) {        if (mCamera == null) return;        if (b == mRunInBackground) return;        if(!b && !isAttachedWindow){            Toast.makeText(context, "Vew未依附在Window,无法显示", Toast.LENGTH_SHORT).show();            return;        }        mRunInBackground = b;        if (b)            setVisibility(View.GONE);        else            setVisibility(View.VISIBLE);    }    /**     * ___________________________________开关闪光灯______________________________________     **/    public void switchLight(boolean open) {        if (mCamera == null) return;        try {            if (mCamera != null) {                if (open) {                    Camera.Parameters parameter = mCamera.getParameters();                    if (parameter.getFlashMode().equals("off")) {                        parameter.setFlashMode("torch");                        mCamera.setParameters(parameter);                    } else {                        parameter.setFlashMode("off");                        mCamera.setParameters(parameter);                    }                } else {                    Camera.Parameters parameter = mCamera.getParameters();                    if ((parameter.getFlashMode() != null) &&                            (parameter.getFlashMode().equals("torch"))) {                        parameter.setFlashMode("off");                        mCamera.setParameters(parameter);                    }                }            }        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * ___________________________________以下为拍照模块______________________________________     **/    public void capture() {        if (mCamera == null) return;        if (!FileUtil.isExternalStorageWritable()) {            Toast.makeText(context, "请插入存储卡", Toast.LENGTH_SHORT).show();            return;        }        mCamera.autoFocus(this);    }    @Override    public void onAutoFocus(boolean success, Camera camera) {        if (success) {            try {                mCamera.takePicture(null, null, new Camera.PictureCallback() {                    @Override                    public void onPictureTaken(byte[] data, Camera camera) {                        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);                        Matrix matrix = new Matrix();                        if (mOpenBackCamera) {                            matrix.setRotate(90);                        } else {                            matrix.setRotate(270);                            matrix.postScale(-1, 1);                        }                        bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);                        FileUtil.saveBitmap(bitmap);                        Toast.makeText(context, "拍照成功", Toast.LENGTH_SHORT).show();                        startPreview();                    }                });            } catch (Exception e) {                if (isRecording) {                    Toast.makeText(context, "请先结束录像", Toast.LENGTH_SHORT).show();                }            }        }    }    /**     * ___________________________________以下为视频录制模块______________________________________     **/    MediaRecorder mediaRecorder = new MediaRecorder();    private boolean isRecording = false;    public boolean isRecording() {        return isRecording;    }    public boolean startRecord() {        return startRecord(-1, null);    }    public boolean startRecord(int maxDurationMs, MediaRecorder.OnInfoListener onInfoListener) {        if (mCamera == null) return false;        if (!FileUtil.isExternalStorageWritable()) {            Toast.makeText(context, "请插入存储卡", Toast.LENGTH_SHORT).show();            return false;        }        mCamera.unlock();        mediaRecorder.reset();        mediaRecorder.setCamera(mCamera);        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);        Camera.Size videoSize = CamParaUtil.getSize(mParam.getSupportedVideoSizes(), 1200,                mCamera.new Size(VIDEO_1080[0], VIDEO_1080[1]));        mediaRecorder.setVideoSize(videoSize.width, videoSize.height);        mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);//        mediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());//设置录制预览surface        if (mOpenBackCamera) {            mediaRecorder.setOrientationHint(90);        } else {            if (screenOritation == Configuration.ORIENTATION_LANDSCAPE)                mediaRecorder.setOrientationHint(90);            else                mediaRecorder.setOrientationHint(270);        }        if (maxDurationMs != -1) {            mediaRecorder.setMaxDuration(maxDurationMs);            mediaRecorder.setOnInfoListener(onInfoListener);        }        mediaRecorder.setOutputFile(FileUtil.getMediaOutputPath());        try {            mediaRecorder.prepare();            mediaRecorder.start();            isRecording = true;        } catch (IOException e) {            e.printStackTrace();            return false;        }        return true;    }    public void stopRecord() {        if (!isRecording) return;        mediaRecorder.setPreviewDisplay(null);        try {            mediaRecorder.stop();            isRecording = false;            Toast.makeText(context, "视频已保存在根目录", Toast.LENGTH_SHORT).show();        } catch (IllegalStateException e) {            e.printStackTrace();        }    }    /**_________________________________________________________________________________________**/}



以下几点是值得注意的:

①  需要相机和录音的权限,如果你的target>=23的话,那么你应该判断系统版本,如果大于23则动态申请权限
②  获取到SurfaceView的SurfaceHolder并添加Callback监听,主要为了监听SurfaceView的创建和销毁
    在创建的回调方法中设置Camera的各项参数和启动Camera的预览,在销毁的回调中关闭Camera预览;
③  设置Camera的各项参数是需要其支持的,所以先获取supprot的参数,再从中选择,若非Camera支持的参数
    会造成预览黑屏或者崩溃;
④  SurfaceTexture和SurfaceView的区别:
    SurfaceTexture是从Android3.0(API 11)加入的一个新类。跟SurfaceView很像,可以从camera preview
    或者video decode里面获取图像流(image stream)。SurfaceView从camera读取到的预览(preview)图像流
    一定要输出到一个可见的(Visible)SurfaceView上,而SurfaceTexture在接收图像流后,不需要显示出来。
    所以在有些需求上,比如在Service后台里面获取camera预览帧进行处理就必须使用SurfaceTexture才能实现。
    所以CameraSurfaceView控件的后台运行也是采用SurfaceTexture的,但是这里我要补充一点的是,当有可见的
    SurfaceTexture,通过Camera.setPreviewTexture(surfaceTexture)也是可以实现显示Camera预览的。
⑤  拍照是先调用聚焦,回调成功后在调用Camera的takePicture方法实现的,大部分机型都能做到在录制视频的同
    时进行拍照,部分机型不行,所以得先关闭录制,这里已经做了相应的处理。
⑥  录制视频这块请严格按照格式进行初始化,因为这个初始化环节每个步骤基本是必须的,而且顺序也不能改变
    否则会崩溃。
⑦  前、后台切换时,如果是通过代码里实例CameraSurfaceView对象并且没添加到window是无法切换为前台的,这
    种情况适用于在service里面实时获取Camera预览帧的情况,注意添加
    mCamera.addCallbackBuffer(previewBuffer);
    mCamera.setPreviewCallbackWithBuffer(previewCallback);
    在Camera.PreviewCallback previewCallback的onPreviewFrame方法里也要添加
    mCamera.addCallbackBuffer(previewBuffer);
⑧  前后置摄像头切换比较简单,关闭当前Camenra,通过findCamera方法找到摄像头ID并打开,然后开启预览即可。


源码传送门:https://github.com/qq542391099/CameraSurfaceView-master


0 0