Android Cameara2 + MediaRecorder 完成录像功能

news2025/6/1 4:21:03

一、打开相机、预览

打开相机预览流程是Camera2的默认流程
可参考:https://blog.csdn.net/kk3087961/article/details/135616576


二、开启录像功能

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

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。
   

/**
     * 初始化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);
        }
    }


3. 创建相机录像混合会话    

/**
     * 创建相机录像会话
     */
    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());
        }
    }

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

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.停止录像释放资源

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.重新创建预览会话开启预览
// 创建相机预览会话
   

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


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

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.");
        }
    };

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2392253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python打卡day39

知识点回顾 图像数据的格式:灰度和彩色数据模型的定义显存占用的4种地方 模型参数梯度参数优化器参数数据批量所占显存神经元输出中间状态 batchisize和训练的关系 课程代码: # 先继续之前的代码 import torch import torch.nn as nn import torch.opti…

3.8.5 利用RDD统计网站每月访问量

本项目旨在利用Spark RDD统计网站每月访问量。首先,创建名为“SparkRDDWebsiteTraffic”的Maven项目,并添加Spark和Scala的依赖。接着,编写Scala代码,通过SparkContext读取存储在HDFS上的原始数据文件,使用map和reduce…

尚硅谷redis7 49-51 redis管道之理论简介

前提redis事务和redis管道有点像,但本质上截然不同 49 redis管道之理论简介 面试题 如何优化频繁命令往返造成的性能瓶颈? redis每秒可以承受8万的写操作和接近10万次以上的读操作。每条命令都发送、处理、返回,能不能批处理一次性搞定呢…

openEuler安装MySql8(tar包模式)

操作系统版本: openEuler release 22.03 (LTS-SP4) MySql版本: 下载地址: https://dev.mysql.com/downloads/mysql/ 准备安装: 关闭防火墙: 停止防火墙 #systemctl stop firewalld.service 关闭防火墙 #systemc…

基于python,html,flask,echart,ids/ips,VMware,mysql,在线sdn防御ddos系统

详细视频:【基于python,html,flask,echart,ids/ips,VMware,mysql,在线sdn防御ddos系统-哔哩哔哩】 https://b23.tv/azUqQXe

Git:现代软件开发的基石——原理、实践与行业智慧·优雅草卓伊凡

Git:现代软件开发的基石——原理、实践与行业智慧优雅草卓伊凡 一、Git的本质与核心原理 1. 技术定义 Git是一个分布式版本控制系统(DVCS),由Linus Torvalds在2005年为管理Linux内核开发而创建。其核心是通过快照(Sna…

NLua性能对比:C#注册函数 vs 纯Lua实现

引言 在NLua开发中,我们常面临一个重要选择:将C#函数注册到Lua环境调用,还是直接在Lua中实现逻辑? 直觉告诉我们,C#作为编译型语言性能更高,但跨语言调用的开销是否会影响整体性能?本文通过基准…

【计算机网络】第2章:应用层—Web and HTTP

目录 一、Web 与 HTTP 二、总结 (一)Web 的定义与功能 (二)HTTP 协议的定义与功能 (三)HTTP 协议的核心机制 1. HTTP 请求与响应流程 2. HTTP 的连接类型 3. HTTP 的状态码 (四&#xf…

数字孪生技术赋能西门子安贝格工厂:全球智能制造标杆的数字化重构实践

在工业4.0浪潮席卷全球制造业的当下,西门子安贝格电子制造工厂(Electronic Works Amberg, EWA)凭借数字孪生技术的深度应用,构建起全球制造业数字化转型的典范。这座位于德国巴伐利亚州的“未来工厂”,通过虚实融合的数…

【图像处理基石】立体匹配的经典算法有哪些?

1. 立体匹配的经典算法有哪些? 立体匹配是计算机视觉中从双目图像中获取深度信息的关键技术,其经典算法按技术路线可分为以下几类,每类包含若干代表性方法: 1.1 基于区域的匹配算法(Local Methods) 通过…

day12 leetcode-hot100-19(矩阵2)

54. 螺旋矩阵 - 力扣(LeetCode) 1.模拟路径 思路:模拟旋转的路径 (1)设计上下左右方向控制器以及边界。比如zy1向右,zy-1向左;sx1向上,sx-1向下。上边界0,下边界hang-1&a…

密钥管理系统在存储加密场景中的深度实践:以TDE透明加密守护文件服务器安全

引言:数据泄露阴影下的存储加密革命 在数字化转型的深水区,企业数据资产正面临前所未有的安全挑战。据IBM《2025年数据泄露成本报告》显示,全球单次数据泄露事件平均成本已达465万美元,其中存储介质丢失或被盗导致的损失占比高达…

webpack打包基本配置

需要的文件 具体代码 webpack.config.js const path require(path);const HTMLWebpackPlugin require(html-webpack-plugin);const {CleanWebpackPlugin} require(clean-webpack-plugin); module.exports {mode: production,entry: "./src/index.ts",output: {…

酷派Cool20/20S/30/40手机安装Play商店-谷歌三件套-GMS方法

酷派Cool系列主打低端市场,系统无任何GMS程序,也不支持直接开启或者安装谷歌服务等功能,对于国内部分经常使用谷歌服务商店的小伙伴非常不友好。涉及机型有酷派Cool20/Cool20S /30/40/50/60等旗下多个设备。好在这些机型运行的系统都是安卓11…

LabVIEW旋转机械智能监测诊断系统

采用 LabVIEW 开发旋转机械智能监测与故障诊断系统,通过集品牌硬件与先进信号处理技术,实现旋转机械振动信号的实时采集、分析及故障预警。系统突破传统监测手段的局限性,解决了复杂工业环境下信号干扰强、故障特征提取难等问题,为…

【芯片设计中的跨时钟域信号处理:攻克亚稳态的终极指南】

在当今芯片设计中,多时钟域已成为常态。从手机SoC到航天级FPGA,不同功能模块运行在各自的时钟频率下,时钟域间的信号交互如同“语言不通”的对话,稍有不慎就会引发亚稳态、数据丢失等问题。这些隐患轻则导致功能异常,重…

接地气的方式认识JVM(一)

最近在学jvm,浮于表面的学了之后,发现jvm并没有我想象中的那么神秘,这篇文章将会用接地气的方式来说一说这些jvm的相关概念以及名词解释。 带着下面两个问题来阅读 认识了解JVM大致有什么在代码运行时的都在背后做了什么 JVM是个啥&#xf…

JAVA:Kafka 消息可靠性详解与实践样例

🧱 1、简述 Apache Kafka 是高吞吐、可扩展的流处理平台,在分布式架构中广泛应用于日志采集、事件驱动和微服务解耦场景。但在使用过程中,消息是否会丢?何时丢?如何防止丢? 是很多开发者关心的问题。 Kafka 提供了一套完整的机制来保障消息从生产者 ➜ Broker ➜ 消费…

Electron 桌面程序读取dll动态库

序幕:被GFW狙击的第一次构建 当我在工位上输入npm install electron时,控制台跳出的红色警报如同数字柏林墙上的一道弹痕: Error: connect ETIMEDOUT 104.20.22.46:443 网络问题不用愁,请移步我的另外文章进行配置:…

HTTP 与 HTTPS 深度解析:原理、实践与大型项目应用

1. HTTP 与 HTTPS 基础概念 1.1 HTTP(超文本传输协议) 定义:应用层协议,基于 TCP/IP 通信,默认端口 80 特点: 无状态协议(需 Cookie/Session 维护状态) 明文传输(易被…