Android 开发 MediaRecorder视频录制入门

Stella981
• 阅读 632

前言

  MediaRecorder是Android SDK提供用于录制音视频,关于音频的录制在我另一篇博客里已经介绍.传送门: https://www.cnblogs.com/guanxinjing/p/10976026.html ,而这篇博客将介绍MediaRecorder视频录制的入门和一些MediaRecorder视频录制的深坑.为什么只介绍简单的录制视频的入门操作,因为MediaRecorder在实际开发的时候肯定还需要配合Camera来使用.而Camera这个大坑又有Camera1和Camera2,所以我们需要分篇幅来介绍Camera1和Camera2与MediaRecorder的使用方式.

  虽然是入门但是还是需要分2个录制操作来说明

  •   MediaRecorder简单的录制视频(不设置Camera)
  • MediaRecorder设置Camera的录制视频(这里指的是Camera1)

  虽然这2个在简单录制视频的时候操作没啥区别,但是设置Camera录制有一个坑,秉承着写博客就是为了记录深坑避免下次掉坑的精神,所以我要抓出来单独说明.

MediaRecorder简单的录制视频

实现流程

  1. 开启硬件加速(因为个人使用TextureView)
  2. 获取权限
  3. 初始化MediaRecorder
  4. 配置MediaRecorder
  5. 开始录制视频
  6. 停止录制视频
  7. 暂停录制与恢复录制
  8. 销毁释放

开启硬件加速

因为TextureView来预览图像使用效果会比SurfaceView与SurfaceTexture,在预览的时候更不会那么卡顿.但是使用它需要开启硬件加速,但是现在的手机设备上基本上都支持硬件加速,所以主流是使用TextureView.下面就是开启硬件加速的方式,在AndroidManifest.xml里给需要硬件加速的activity添加android:hardwareAccelerated="true" 属性

<activity android:name=".MediaRecorderVideoActivity"
            android:hardwareAccelerated="true"></activity>

获取权限

<!--音频录制权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!--相机权限-->
    <uses-permission android:name="android.permission.CAMERA" />
    <!--读取和写入存储权限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

如果是Android5.0版本这4个权限是需要动态授权的.

初始化MediaRecorder

private void initMediaRecorder(){
        mMediaRecorder = new MediaRecorder();
    }

很简单就是new一个MediaRecorder

配置MediaRecorder

private void configMediaRecorder(){
        File videoFile = new File(getExternalCacheDir(),"video.mp4");
        Log.e(TAG, "文件路径="+videoFile.getAbsolutePath());
        if (videoFile.exists()){
            videoFile.delete();
        }
      mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);//设置音频输入源  也可以使用 MediaRecorder.AudioSource.MIC
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//设置视频输入源
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//音频输出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//设置音频的编码格式  
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);//设置图像编码格式

//        mMediaRecorder.setVideoFrameRate(30);//要录制的视频帧率 帧率越高视频越流畅 如果设置设备不支持的帧率会报错  按照注释说设备会支持自动帧率所以一般情况下不需要设置
//        mMediaRecorder.setVideoSize(1280,1920);//设置录制视频的分辨率  如果设置设备不支持的分辨率会报错
     mMediaRecorder.setVideoEncodingBitRate(8*1920*1080); //设置比特率,比特率是每一帧所含的字节流数量,比特率越大每帧字节越大,画面就越清晰,算法一般是 5 * 选择分辨率宽 * 选择分辨率高,一般可以调整5-10,比特率过大也会报错
        mMediaRecorder.setOrientationHint(90);//设置视频的摄像头角度 只会改变录制的视频文件的角度(对预览图像角度没有效果)

        Surface surface = new Surface(mTextureView.getSurfaceTexture());
        mMediaRecorder.setPreviewDisplay(surface);//设置拍摄预览
        mMediaRecorder.setOutputFile(videoFile.getAbsolutePath());//MP4文件保存路径
        
    }

配置MediaRecorder是坑比较多的地方,现在我们来一一说下这些坑

  • 坑1:  首先MediaRecorder的配置是有顺序要求的,随便配置参数将会报错,一般顺序是 设置音频输入源与视频输入源  >  设置音频编码格式/视频编码格式/音频输出格式 > 设置分辨率/帧率/摄像头角度 > 添加预览 > 添加保存文件路径
  • 坑2:  在上面的代码上我注释了setVideoFrameRate(int rate) 与 setVideoSize(int width, int height) 这2个方法,设置录制视频帧率与设置视频分辨率.因为这2个设置的参数都是需要手机设备支持所输入的的值的,随便设置将会抛出 start failed -19 的错误
  • 坑3:  setOrientationHint()方法设置角度不会改变预览图像的角度
  • 坑4:  setPreviewDisplay()设置预览,其实只预览录制过程中的图像,停止录制,相机图像预览也停止了.如果想要一直预览就需要操作Camera来实现
  • 坑5:  关于各种参数设置的格式(设置视频输入源,音频输入源,音频输出格式,音频编码格式,图像编码),这里如果你只是实现demo推荐全部使用****DEFAULT,上面的demo图像编码和音频编码使用MP4格式只是一个demo告诉你有很多其他格式.很多情况系统虽然提供了很多格式,但是下各个设备支持的格式是不同的.如果你是大量适配机型最好使用默认格式或者AAC音频编码格式与H264视频编码格式(目前这2个格式最通用),如果是只做单一设备开发,可以使用指定格式.
  • 坑6  当你选择默认编码格式录制视频后发现在Ios设备上无法自动播放? 其实是因为默认编码格式选择了IOS设备不支持的格式,Ios只支持AAC音频编码格式H264视频编码格式,在正常开发的时候建议使用这2个编码格式(他们是目前最通用的编码格式)

注意!上面的坑5关于参数配置的问题,当你发现出现MediaRecorder: start failed: -38 或者其他数值的报错,一定会去百度,这时候-38报错 百度可能会告诉是MIC没释放或者Camera没释放(Camera没释放的问题我下面那个demo会说明),这的确有可能,但是还有另外一种可能是设置setAudioEncoder或者setVideoEncoder在或者setAudioSource 上出现了问题,比如一些设备是支持setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);但是换调另外一个设备上就不支持了,所以这个时候请切换成setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);默认模式,使用还是重新提醒你如果有需要适配大量机型的APP那么尽量选择默认格式.如果你只是为单一Android设备开发你可以指定格式.

小提醒~如果你想一进入Activity就配置MediaRecorder好,那么你就需要设置TextureView的监听setSurfaceTextureListener(SurfaceTextureListener listener),必需它先启动准备好之后在配置MediaRecorder,否则会报错的因为TextureView在启动activity后需要一段时间初始化启动

如果你需要切换摄像头,那么你还需要注意重新配置MediaRecorder时还需要切换角度:

if (mCurrentCameraFacing){ //相机前后面向
            mMediaRecorder.setOrientationHint(270);
        }else {
            mMediaRecorder.setOrientationHint(90);
        }

开始录制视频

private void startRecorder(){
        configMediaRecorder();//配置MediaRecorder  因为每一次停止录制后调用重置方法后都会取消配置,所以每一次开始录制都需要重新配置一次
        try {
            mMediaRecorder.prepare();//准备
            mMediaRecorder.start();//开启
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

注意!每一次停止录制后调用重置方法后都会取消配置,所以每一次开始录制都需要重新配置一次

停止录制视频

private void stopRecorder(){
        mMediaRecorder.stop();//暂停
        mMediaRecorder.reset();//重置 重置后将进入空闲状态,再次启动录制需要重新配置MediaRecorder
    }

暂停录制与恢复录制

暂停录制,注意这里是pause()方法,不是stop()

private void pauseRecorder(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            mMediaRecorder.pause();//暂停
        }
    }

恢复录制

private void resumeRecorder(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            mMediaRecorder.resume();//恢复
        }
    }

销毁释放

private void destroy(){
        if (mMediaRecorder != null){
            mMediaRecorder.stop();
            mMediaRecorder.release();//释放 释放之前需要先调用stop()
            mMediaRecorder = null;
        }
    }

MediaRecorder设置Camera的录制视频

添加Camera来录制视频本来是另外一个篇幅应该说的事情,但是有个坑不得不在这里先说. 其他初始化和开启/停止/都与上面的介绍一致,下面我们来说说万恶的MediaRecorder配置:

private void configMediaRecorder(){
        File videoFile = new File(getExternalCacheDir(),"video.mp4");
        Log.e(TAG, "文件路径="+videoFile.getAbsolutePath());
        if (videoFile.exists()){
            videoFile.delete();
        }

        Camera camera = Camera.open();
        camera.unlock();
        mMediaRecorder.setCamera(camera);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置音频输入源
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//设置视频输入源
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//音频输出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//设置音频的编码格式
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);//设置图像编码格式
        mMediaRecorder.setOrientationHint(90);//设置视频的摄像头角度 只会改变录制的视频角度
        Surface surface = new Surface(mTextureView.getSurfaceTexture());
        mMediaRecorder.setPreviewDisplay(surface);
        mMediaRecorder.setOutputFile(videoFile.getAbsolutePath());


    }

  也是一个简单的配置,也就是多了有摄像头的代码,如果你需要选择摄像头可以自己获取摄像头信息来判断前后在获取对应id,打开对应摄像头,作为简单的demo就暂时不演示选择摄像头的代码.

  然后重点来了这里有一个关键,你不会想到Camera.open();执行后摄像头居然是锁定状态的,就算是明白打开后就是锁定,但是你也不会发现在设置mMediaRecorder.setCamera(camera);之前是需要将摄像头解除锁定 camera.unlock();! 是的,就是这行代码困扰我了一下午!!!!!! 所以重点!重点!重点! 就是camera.unlock(); 最蛋疼的是如果你不做解除锁定的操作报错的提示是 start failed -19, 这个报错提示成功的让我反反复复研究是不是分辨率尺寸设置有问题........

  而这个Camera.unlock()在官方注释里也有说明在启动在设置之前先需要解锁摄像头,如下:

* <p>Use this function to switch quickly between preview and capture mode without a teardown of
     * the camera object. {@link android.hardware.Camera#unlock()} should be called before
     * this. Must call before {@link #prepare}.</p>

setProfile

这个功能其实是相机预设的一些录制质量,格式和分辨率,官方注释说:

使用CamcorderProfile对象中的设置进行录制。这种方法应该在设置视频和音频源之后和setOutputfile()之前调用。如果使用延时摄像机配置文件,音频相关源或录制参数被忽略。

我们看到关键CamcorderProfile对象,那么看看什么CamcorderProfile的解释:

摄像机应用程序的预定义摄像机配置文件设置,这些设置是只读的。其实就是预设的一些摄像头配置设置

预设的属性有这些:

  • QUALITY_LOW
  • QUALITY_HIGH
  • QUALITY_QCIF
  • QUALITY_CIF
  • QUALITY_480P
  • QUALITY_720P
  • QUALITY_1080P
  • QUALITY_2160P
  • QUALITY_TIME_LAPSE_LOW
  • QUALITY_TIME_LAPSE_HIGH
  • QUALITY_TIME_LAPSE_QCIF
  • QUALITY_TIME_LAPSE_CIF
  • QUALITY_TIME_LAPSE_480P
  • QUALITY_TIME_LAPSE_720P
  • QUALITY_TIME_LAPSE_1080P
  • QUALITY_TIME_LAPSE_2160P
  • QUALITY_HIGH_SPEED_LOW
  • QUALITY_HIGH_SPEED_HIGH
  • QUALITY_HIGH_SPEED_480P
  • QUALITY_HIGH_SPEED_720P
  • QUALITY_HIGH_SPEED_1080P
  • QUALITY_HIGH_SPEED_2160P

使用代码:

if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) {
                CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
                profile.videoBitRate = mPreviewSize.getWidth() * mPreviewSize.getHeight();
                mMediaRecorder.setProfile(profile);
                mMediaRecorder.setPreviewDisplay(new Surface(mTextureView.getSurfaceTexture()));
            } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
                CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
                profile.videoBitRate = mPreviewSize.getWidth() * mPreviewSize.getHeight();
                mMediaRecorder.setProfile(profile);
                mMediaRecorder.setPreviewDisplay(new Surface(mTextureView.getSurfaceTexture()));
            } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QVGA)) {
                mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_QVGA));
                mMediaRecorder.setPreviewDisplay(new Surface(mTextureView.getSurfaceTexture()));
            } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_CIF)) {
                mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_CIF));
                mMediaRecorder.setPreviewDisplay(new Surface(mTextureView.getSurfaceTexture()));
            } else {
                mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
                mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
                mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
                mMediaRecorder.setVideoEncodingBitRate(10000000);
                mMediaRecorder.setVideoFrameRate(30);
                mMediaRecorder.setVideoSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
}

end

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
VSCode配置FiraCode和更纱黑体字体
!(https://oscimg.oschina.net/oscnet/c7bb62d935ceb01d3b7fe176322e84ae00d.png)Fira Code下载到FiraCode字体的GitHub(https://www.oschina.net/action/GoToLink?urlhttps%
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
Python 环境搭建
pythonbug集目录\toc\00python模块下载地址pyhton模块下载地址(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.lfd.uci.edu%2F%7Egohlke%2Fpythonlibs%2F)01pythonpip
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这