美文网首页camera2
Android自定义视频录制

Android自定义视频录制

作者: JaydenWfj | 来源:发表于2018-11-01 22:19 被阅读62次

    概览

    Android 进行录像的一个简单方法是:创建一个MediaStore.ACTION_VIDEO_CAPTURE的Intent来调起一个已有的相机应用。

    但有时候我们需要自己定义录制界面,或者需要一些高级的功能,那么Intent方式就不能满足我们的要求的。

    因此,我们可以使用Android framework为我们提供的Camera和Camera2 APIs来直接操作相机设备。

    本章我们主要介绍,如何使用Camera 、Camera2以及MediaRecorder来录制视频。

    权限设置

    请求使用相机的权限:

    <uses-permission android:name="android.permission.CAMERA" />
    

    在配置文件中声明使用相机功能:

    <uses-feature android:name="android.hardware.camera" />
    

    具体相机功能可以查看以下链接:

    Feature Reference

    在配置文件中声明使用相机的自动对焦和闪光灯功能:

    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.flash" android:required="false"/>
    
    

    请求访问闪光灯权限:

     <uses-permission android:name="android.permission.FLASHLIGHT"/>
    

    要在SD卡上存储获取读取视频 :

     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    

    录制视频和音频权限:

     <uses-permission android:name="android.permission.RECORD_AUDIO"/>
     <uses-permission android:name="android.permission.RECORD_VIDEO"/>
    

    注意:Android6.0及以上需动态权限申请

    Camera

    涉及相关功能类:

    Camera:管理相机API(开启、关闭预览、设置相机参数)

    Camera.CameraInfo:相机信息(前置or后置摄像头等)

    //i为摄像头的id,通过 Camera.getNumberOfCameras()
    Camera.getCameraInfo(i, mCameraInfo);//获取对于摄像头的相机信息
    

    Camera.Parameters :相机特性 (自动对焦、闪光、预览大小、图片格式、图片大小等)

    Camera2

    涉及相关功能类:

    CameraManager:摄像头管理类,用于检测、打开系统摄像头,通过getCameraCharacteristics(cameraId)可以获取摄像头特征

    CameraCharacteristics :相机特性类,例如,是否支持自动调焦,是否支持zoom,是否支持闪光灯一系列特征

    CameraDevice:相机设备,类似早期的camera类

    CameraRequest:一次捕获的请求,可以设置一些列的参数,用于控制预览和拍照参数,例如:对焦模式,曝光模式,zoom参数等等

    CameraCaptureSession:用于创建预览、拍照的Session类。通过它的setRepeatingRequest()方法请求连续预览 , 通过它的capture()方法控制拍照动作。

    MediaRecorder

    用于录制视频和音频的一个类。基于下图的状态机类控制录制过程:

    [图片上传失败...(image-4b940a-1541081884311)]

    状态说明:

    Initial:当new MediaRecorder()或者在除Released状态外的其它状态通过调用reset()方法后,MediaRecorder进入Initial状态

    Initialized:在Initial状态下,通过设定视频源setVideoSource()或者音频源setAudioSource()之后,状态切换为Initialized状态

    DataSourceConfigured:在Initialized状态下,可以通过setOutputFormat()方法设置输出格式,此时

    MediaRecorder转换为DataSourceConfigured状态。 数据源配置状态,这期间可以设定编码方式、输出文件、视频大小、视频帧率、预览显示等等。

    Prepared:装备就绪状态,在DataSourceConfigured状态下,调用prepare()方法进入该状态。

    Recording:录制状态,在Prepared状态下,调用start()方法进入该状态。通过stop()方法或reset()方法回到Initial状态。

    Released:释放状态,可以通过在Initial状态调用release()方法来进入这个状态,这时将会释放和MediaRecorder对象关联的所有资源。

    Error:错误状态,当错误发生的时候进入这个状态,它可以通过reset()方法进入Initial状态。

    注意:使用MediaRecorder进行视频音频录制时,必须严格按照上面的状态图规定的函数先后调用顺序,在不同的状态调用不同的函数,否则会抛异常IllegalStateException

    VCamera

    概述:提供视频录制、后滤镜、炫酷 MV 主题、后期强大的 FFmpeg命令行支持,可实现水印、音量控制等诸多功能

    使用文档

    音视频单独采集

    ts片段转mp4

    ffmpeg -i concat:/sdcard/WeiXinRecordedDemo/1531060034234/0.ts -c copy -bsf:a aac_adtstoasc -y /sdcard/WeiXinRecordedDemo/1531060034234/0.mp4

    预览功能

    预览一般步骤流程:

    流程说明:

    • 监测和访问相机:检查相机是否存在,若存在则请求访问
    • 获取、设置相关参数
    • 设置一个预览类,如SurfaceView or TextureView
    • 设置监听,一旦Surface可用,我们就可以开启预览。当Surface销毁我们就关闭预览
    • 界面销毁时,释放预览相关资源和正确释放相机

    Camera和Camera2各自是如何实现预览功能:

    请求访问相机:

    Camera实现:

    //获取设备物理可用Camera
    int count = Camera.getNumberOfCameras();
    //检测是哪个相机
    for (int i = 0; i < count; i++) {
        Camera.getCameraInfo(i, mCameraInfo);
        if (mCameraInfo.facing == mFacing) {
            mCameraId = i;
            return true;
        }
    }
    //开启
    mCamera = Camera.open(mCameraId);
    

    Camera2实现:

    //获取设备物理可用Camera
    final String[] ids = mCameraManager.getCameraIdList();
    ...省略部分代码
    
    //找到我们想要的那个Camera
    for (String id : ids) {
        CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
        ...
        Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);
        if (internal == null) {
            return false;
        }
        if (internal == internalFacing) {
            mCameraId = id;
            mCameraCharacteristics = characteristics;
            return true;
        }
    }
    

    通过CameraManager对象,调用openCamera方法开启相机:

    try {
        mCameraManager.openCamera(mCameraId, mCameraDeviceCallback, null);
        return true;
    } catch (CameraAccessException e) {
        //throw new RuntimeException("Failed to open camera: " + mCameraId, e);
        return false;
    }
    

    需要三个参数:

    String cameraId:相机设备的唯一标识符
    CameraDevice.StateCallback callback:相机打开的回调方法
    Handler handler:回调方法在哪个线程处理
    

    获取相关参数:

    相机特性:

     1. Camera.Parameters mCameraParameters = mCamera.getParameters()
     
     2. CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
    

    获取支持的预览大小列表、视频大小列表、图片大小列表:

     1. mCameraParameters = mCamera.getParameters();
      List<Size> previewSizeList = mCameraParameters.getSupportedPreviewSizes();
      视频列表:
      List<Size> videoSizeList = mCameraParameters.getSupportedVideoSizes();
      图片列表:
      List<Size> pictureSizeList =  mCameraParameters.getSupportedPictureSizes()
      
     2.  StreamConfigurationMap map =        mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 
      Size[] previewSizeList = map.getOutputSizes(mPreview.getOutputClass());
      视频列表:
      Size[] videoSizeList = map.getOutputSizes(MediaRecorder.class)
      图片列表:
      Size[] pictureSizeList = map.getOutputSizes(SurfaceTexture.class)
    

    设置参数:

    Camera主要设置在Camera.Parameters 
    mCameraParameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight());
    mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
    mCamera.setParameters(mCameraParameters);
    
    Camera2主要设置在CaptureRequest.Builder
    mPreviewRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
    mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
    

    设置预览View:

    Camera实现:

    try {
        if (mPreview.getOutputClass() == SurfaceHolder.class) {
            ...
            mCamera.setPreviewDisplay(mPreview.getSurfaceHolder());
            ...
        } else {//Android 4.0(API 14)及以上
            mCamera.setPreviewTexture((SurfaceTexture) mPreview.getSurfaceTexture());
        }
    } catch (IOException e) {
        //throw new RuntimeException(e);
    }
    

    Camera2实现:

    try {
        mPreview.setBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());//Camera2 API有效
        Surface surface = mPreview.getSurface();
    
        //图像内容将输出到Preview的surface和ImageReader的surface中
        mPreviewRequestBuilder.addTarget(surface);
        mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
    } catch (CameraAccessException e) {
    }
    

    开启预览:

    Camera实现:

    mCamera.startPreview();
    

    Camera2实现:

    //调用CameraDevicea的createCaptureRequest创建一个请求
    mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    
    mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionCallback, null);
    
    //调用如下方法才能不断预览
     mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);
    

    接收预览每帧数据:

    //Camera实现:
    mCamera.setPreviewCallback(new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
            mCallback.onPreviewData(data);//预览每帧数据回调
        }
    });
    
    //Camera2
      mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(),
                    ImageFormat.YUV_420_888, /*maxImages*/2);//如果是预览则图片格式不要设置为JPEG,否则会一直拍照,造成预览卡
      mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
      
      private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
                = new ImageReader.OnImageAvailableListener() {
    
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = reader.acquireNextImage();
                ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                byte[] bytes = new byte[buffer.remaining()];
                buffer.get(bytes);
                mCallback.onPreviewData(bytes);//预览每帧数据回调
                image.close();//记得关闭,否则 抛出异常java.lang.IllegalStateException: maxImages (2) has //                           already been acquired, call #close before acquiring more.
            }
        };
      
    

    停止预览:

    //Camera实现:
    if (mCamera != null) {
        mCamera.stopPreview();
    }
    
    //Camera2
    if (mCaptureSession != null) {
        mCaptureSession.close();
        mCaptureSession = null;
    }
    

    正确释放相机资源:

    //Camera实现:
    public void release() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.release();
            mCamera = null;
        }
    }
    
    //Camera2
    public void release() {
        if (mCamera != null) {
            mCamera.close();
            mCamera = null;
        }
    }
    

    视频录制

    Camera实现视频录制,必须严格按照一定的调用顺序。具体可查看Capturing videos

    1. 开启相机:Camera.open()
    2. 设置预览View:setPreviewDiaplay()
    3. 开启预览:startPreview()
    4. 开始录制:
      1. unlock camera
      2. 配置MediaRecorder
      3. MediaRecorder prepare
      4. 开启录制
    5. 停止录制
    6. 停止预览
    7. 释放相机

    Camera和Camera2使用MediaRecorder录制视频的流程对比:

    相关文章

      网友评论

        本文标题:Android自定义视频录制

        本文链接:https://www.haomeiwen.com/subject/xiufxqtx.html