Android Cameara2 + MediaRecorder 完成录像功能

一、打开相机、预览

打开相机预览流程是Camera2的默认流程

可参考:https://blog.csdn.net/kk3087961/article/details/135616576

二、开启录像功能

开启录像主要包括以下3步:

java 复制代码
private void startRecording() {
        // 1. 停止预览并关闭会话
        if (mCameraSession != null) {
            try {
                mCameraSession.stopRepeating();
                mCameraSession.close();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
        //2. 配置MediaRecorder
        setUpMediaRecord();
        // 3. 创建混合会话
        createCameraRecordSession();
    }
  1. 停止预览并关闭会话。

  2. 配置MediaRecorder。

java 复制代码
/**
     * 初始化MediaRecord
     */
    private void setUpMediaRecord() {
        Logger.i(TAG, "setUpMediaRecord() 11 w:" + Config.SHOOT_PIC_WIDTH + " h:" + Config.SHOOT_PIC_HEIGHT);
        mMediaRecorder = new MediaRecorder();
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mMediaRecorder.setVideoSize(Config.SHOOT_PIC_WIDTH, Config.SHOOT_PIC_HEIGHT);
        mMediaRecorder.setVideoFrameRate(30);

        File videoFile = new File(Config.DEFAULT_VIDEO_DIR_PREFIX + StringUtil.getTimeTextWithSSS(System.currentTimeMillis()) + ".mp4");
        mMediaRecorder.setOutputFile(videoFile.getAbsolutePath());
        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
  1. 创建相机录像混合会话
java 复制代码
/**
     * 创建相机录像会话
     */
    private void createCameraRecordSession() {
        // 获取TextureView纹理视图的表面纹理
        SurfaceTexture texture = mPlayerView.getSurfaceTexture();
        // 设置表面纹理的默认缓存尺寸
        texture.setDefaultBufferSize(mPreViewSize.getWidth(), mPreViewSize.getHeight());
        // 创建一个该表面纹理的表面对象
        mSurface = new Surface(texture);
        try {
            synchronized (this) {
                if (isCameraClosed) {//创建Session逻辑都是在mHandler中异步执行的,所以需要对isCameraClosed部分进行同步
                    Logger.w(TAG, "createCameraPreviewSession : method close has been invoked.");
                    return;
                }
                mRecordBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
                // 把纹理视图添加到预览目标
                mRecordBuilder.addTarget(mSurface);
                mRecordBuilder.addTarget(mMediaRecorder.getSurface());
                // 设置自动对焦模式
                mRecordBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                // 设置自动曝光模式
                mRecordBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                // 开始对焦
                mRecordBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                        CameraMetadata.CONTROL_AF_TRIGGER_START);
                // 设置预览的方向
                mRecordBuilder.set(CaptureRequest.JPEG_ORIENTATION, (mCameraType == CameraCharacteristics.LENS_FACING_FRONT) ? 90 : 270);
                //配置 ImageReader
                if (mImageReader != null) {
                    mImageReader.close();
                    mImageReader = null;
                }
                mImageReader = ImageReader.newInstance(Config.SHOOT_PIC_WIDTH,
                        Config.SHOOT_PIC_HEIGHT, ImageFormat.JPEG, 1);
                mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
                    @Override
                    public void onImageAvailable(ImageReader reader) {
                        //处理拍照回调,保存拍照数据
                        Bitmap bitmap = getByteFromReader(reader);
                        Log.i(TAG, "onImageAvailable() null != bitmap?" + (null != bitmap));
                        //bitmap 判空处理放在doCaptureAfterGetImage中处理
                        doCaptureAfterGetImage(bitmap);
                    }
                }, null);

                // 创建一个相片捕获会话。此时预览画面显示在纹理视图上,该对象负责管理处理预览请求和拍照请求以及录像
                mCameraDevice.createCaptureSession(Arrays.asList(mSurface, mImageReader.getSurface(), mMediaRecorder.getSurface()),
                        mVidioSessionStateCallback, mHandler);
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
            Logger.w(TAG, "createCameraPreviewSession: exc=" + e.getLocalizedMessage());
            onCameraError();
        } catch (Exception e) {
            e.printStackTrace();
            onCameraError();
            Logger.e(TAG, "createCameraPreviewSession: exception." + e.getLocalizedMessage());
        }
    }

// 录像会话配置就绪后,将预览画面呈现到手机屏幕上,然后开启录像

java 复制代码
private CameraCaptureSession.StateCallback mVidioSessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(CameraCaptureSession session) {
            try {
                synchronized (this) {
                    if (isCameraClosed) {//回调逻辑都是在mHandler中顺序执行的,所以需要对isCameraClosed部分进行同步
                        Logger.w(TAG, "mVidioSessionStateCallback#onConfigured : method close has been invoked.");
                        return;
                    }
                    Logger.i(TAG, "mVidioSessionStateCallback : onConfigured.");
                    mCameraSession = session;
                    // 设置连拍请求。此时预览画面只会发给手机屏幕
                    mCameraSession.setRepeatingRequest(mRecordBuilder.build(), mPreviewCallback, mHandler);
                    mMediaRecorder.start();
                }
                onCameraPrepared();
            } catch (CameraAccessException e) {
                e.printStackTrace();
                Logger.e(TAG, "mVidioSessionStateCallback::onConfigured exc = " + e.getMessage());
                onCameraError();
            } catch (Exception e) {
                e.printStackTrace();
                Logger.e(TAG, "mVidioSessionStateCallback: exception." + e.getLocalizedMessage());
                onCameraError();
            }
        }

        @Override
        public void onConfigureFailed(CameraCaptureSession session) {
            onCameraError();
            Logger.i(TAG, "mVidioSessionStateCallback::onConfigureFailed.");
        }
    };

三、停止录像功能

停止录像主要包括两步

1.停止录像释放资源

java 复制代码
private void stopRecording() {
        Logger.i(TAG, "stopRecording()");
        try {
            mMediaRecorder.stop();
            mMediaRecorder.reset();
            mMediaRecorder.release();
            mCameraSession.stopRepeating();
            mCameraSession.close();
            //mMediaRecorder = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2.重新创建预览会话开启预览

// 创建相机预览会话

java 复制代码
protected void createCameraPreviewSession() {
        // 获取纹理视图的表面纹理
        SurfaceTexture texture = mPlayerView.getSurfaceTexture();
        // 设置表面纹理的默认缓存尺寸
        texture.setDefaultBufferSize(mPreViewSize.getWidth(), mPreViewSize.getHeight());
        // 创建一个该表面纹理的表面对象
        mSurface = new Surface(texture);
        try {
            synchronized (this) {
                if (isCameraClosed) {//创建Session逻辑都是在mHandler中异步执行的,所以需要对isCameraClosed部分进行同步
                    Logger.w(TAG, "createCameraPreviewSession : method close has been invoked.");
                    return;
                }
                mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                // 把纹理视图添加到预览目标
                mPreviewBuilder.addTarget(mSurface);
                // 设置自动对焦模式
                mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                // 设置自动曝光模式
                mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                // 开始对焦
                mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                        CameraMetadata.CONTROL_AF_TRIGGER_START);
                // 设置预览的方向
                mPreviewBuilder.set(CaptureRequest.JPEG_ORIENTATION, (mCameraType == CameraCharacteristics.LENS_FACING_FRONT) ? 90 : 270);
                //配置 ImageReader
                if (mImageReader != null) {
                    mImageReader.close();
                    mImageReader = null;
                }
                mImageReader = ImageReader.newInstance(Config.SHOOT_PIC_WIDTH,
                        Config.SHOOT_PIC_HEIGHT, ImageFormat.JPEG, 1);
                mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
                    @Override
                    public void onImageAvailable(ImageReader reader) {
                        //处理拍照回调,保存拍照数据
                        Bitmap bitmap = getByteFromReader(reader);
                        Log.i(TAG, "onImageAvailable() null != bitmap?" + (null != bitmap));
                        //bitmap 判空处理放在doCaptureAfterGetImage中处理
                        doCaptureAfterGetImage(bitmap);
                    }
                }, null);

                // 创建一个相片捕获会话。此时预览画面显示在纹理视图上,该对象负责管理处理预览请求和拍照请求
                mCameraDevice.createCaptureSession(Arrays.asList(mSurface, mImageReader.getSurface()),
                        mSessionStateCallback, mHandler);
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
            Logger.w(TAG, "createCameraPreviewSession: exc=" + e.getLocalizedMessage());
            onCameraError();
        } catch (Exception e) {
            e.printStackTrace();
            onCameraError();
            Logger.e(TAG, "createCameraPreviewSession: exception." + e.getLocalizedMessage());
        }
    }

// 影像配置就绪后,将预览画面呈现到手机屏幕上

java 复制代码
private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(CameraCaptureSession session) {
            try {
                synchronized (this) {
                    if (isCameraClosed) {//回调逻辑都是在mHandler中顺序执行的,所以需要对isCameraClosed部分进行同步
                        Logger.w(TAG, "mSessionStateCallback#onConfigured : method close has been invoked.");
                        return;
                    }
                    Logger.i(TAG, "SessionStateCallback : onConfigured.");
                    mCameraSession = session;
                    // 设置连拍请求。此时预览画面只会发给手机屏幕
                    mCameraSession.setRepeatingRequest(mPreviewBuilder.build(), mPreviewCallback, mHandler);
                }
                onCameraPrepared();
            } catch (CameraAccessException e) {
                e.printStackTrace();
                Logger.e(TAG, "mSessionStateCallback::onConfigured exc = " + e.getMessage());
                onCameraError();
            } catch (Exception e) {
                e.printStackTrace();
                Logger.e(TAG, "mSessionStateCallback: exception." + e.getLocalizedMessage());
                onCameraError();
            }
        }

        @Override
        public void onConfigureFailed(CameraCaptureSession session) {
            onCameraError();
            Logger.i(TAG, "mSessionStateCallback::onConfigureFailed.");
        }
    };
相关推荐
草莓熊Lotso11 分钟前
Linux 文件描述符与重定向实战:从原理到 minishell 实现
android·linux·运维·服务器·数据库·c++·人工智能
恋猫de小郭17 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
工程师老罗7 小时前
如何在Android工程中配置NDK版本
android
Libraeking10 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位10 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen12312 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs13 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob13 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔13 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei99613 小时前
flutter和Android动画的对比
android·flutter·动画