首页 > 其他分享 >从零开始的Android音视频学习(二)

从零开始的Android音视频学习(二)

时间:2023-11-09 12:06:04浏览次数:40  
标签:mAudioRecord private 音视频 从零开始 RecorderStatus new Android public CHANNEL

AudioRecord录制PCM音频

看一下AudioRecord安卓源码的构造函数(targetAPi29) image.png

主要看一下几个参数

  1. audioSource 声音来源,参考 MediaRecorder.AudioSource 这里的种类还比较多,暂时先使用MIC,其他的后续用到再说吧
  2. sampleRateInHz 音频采样率 注释上说44100Hz是全部设备都支持的,其他的如22050, 16000, 11025可能或有部分设备支持。 44100Hz 即每秒采样44100次
  3. channelConfig 音频通道,即声道数,CHANNEL_IN_MONO单声道,所有设备都支持, CHANNEL_IN_STEREO立体声,多声道,可能是两个声道?
public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;
public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);
  1. audioFormat 音频数据格式,看源码种类也比较多,8位16位32位的pcm 这个类型有多达29种,网上常用的案例都是使用16位的pcm,我们这里先使用ENCODING_PCM_16BIT 16位的PCM
  2. bufferSizeInBytes 音频缓冲区大小,这个注释有点没看懂,全部尺寸的缓冲区当音频数据被录制时? 后面说这个值通过getMinBufferSize这个方法获取。 直接上代码看下吧
  private int mReadMinBufferSize;

    public enum RecorderStatus {
        NO_READY,
        START,
        STOP
    }

    private AudioRecord mAudioRecord;
    private volatile RecorderStatus mStatus;
    private static final int mAudioSource = MediaRecorder.AudioSource.MIC;
    private static final int mSampleRateInHz = 44100;
    private static final int mChannelConfig = AudioFormat.CHANNEL_IN_MONO;
    private static final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
    private final ExecutorService mExecutors = Executors.newSingleThreadExecutor();

    public RecorderStatus getStatus() {
        return mStatus;
    }

    public void startRecordOnMainThread(String filePath) {
        mExecutors.execute(() -> {
            try {
                startRecord(filePath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }


    @SuppressLint("MissingPermission")
    @WorkerThread
    public void startRecord(String filePath) throws IOException {
        if (mStatus == RecorderStatus.START) {
            Log.i(TAG, "startRecord: already in recording");
            return;
        }
        File file = new File(filePath);
        if (file.exists()) {
            boolean delete = file.delete();
            Log.i(TAG, "startRecord: delete already exist audio file result = " + delete);
        }
        String dir = filePath.substring(0, filePath.lastIndexOf(File.separator) + 1);
        if (!new File(dir).exists()) {
            boolean mkdirs = new File(dir).mkdirs();
            Log.i(TAG, "startRecord: mkdir directory result = " + mkdirs + "   and path = " + dir);
        }
        boolean newFile = file.createNewFile();
        Log.i(TAG, "startRecord: create new Audio file result = " + newFile + "   and path = " + filePath);
        mReadMinBufferSize = AudioRecord.getMinBufferSize(mSampleRateInHz, mChannelConfig, mAudioFormat);
        if (mAudioRecord == null) {
            mAudioRecord = new AudioRecord(mAudioSource, mSampleRateInHz, mChannelConfig, mAudioFormat, mReadMinBufferSize);
        }
        mStatus = RecorderStatus.START;
        mAudioRecord.startRecording();
        try (FileOutputStream fos = new FileOutputStream(file)) {
            byte[] buf = new byte[mReadMinBufferSize];
            while (mStatus == RecorderStatus.START) {
                int bufferReadResult = mAudioRecord.read(buf, 0, mReadMinBufferSize);
                if (bufferReadResult != AudioRecord.ERROR_INVALID_OPERATION) {
                    fos.write(buf);
                }
            }
        }
    }

    public void stopRecord() {
        mStatus = RecorderStatus.STOP;
        if (mAudioRecord != null) {
            mAudioRecord.stop();
        }
    }

    public void release() {
        mStatus = RecorderStatus.NO_READY;
        if (mAudioRecord != null) {
            mAudioRecord.release();
            mAudioRecord = null;
        }
    }

使用FFmpeg播放PCM音频

ffplay -ar 44100 -ac 1 -f s16le -i 2023_11_09_11_06_01.pcm
-i 表示指定的输入文件
-f 表示强制使用的格式
-ar 表示播放的音频数据的采样率
-ac 表示播放的音频数据的通道数

标签:mAudioRecord,private,音视频,从零开始,RecorderStatus,new,Android,public,CHANNEL
From: https://blog.51cto.com/u_13893439/8274952

相关文章

  • 【从零开始学习Go语言】八.Go语言的数组切片引用类型与值类型(总结)
    【从零开始学习Go语言】Go语言的数组与切片引用类型与值类型一.数组二.多维数组三.切片四.值类型与引用类型一.数组go语言的数组在之前的一些例子中有引用过,go的数组在创建时需要声明存储数据的类型,长度,并且长度在确定后便不可增加,类似python中的元组数组的声明方式有多种:第一种......
  • Android之签名
    一,AndroidStudio二,通过AndroidStudio进行签名1,Build->  GenerateSignedBundle/APK...         2,我们选择APK3,如果没有签名必须新建签名。3.1,点击“Createnew...”新建签名   3.2, 开始新建签名文件3.2.1,如下图所示:      3.2.2,......
  • Delphi10.4 Android调用相机返回图片调试
    Delphi10.4Android调用相机返回图片调试使用Delphi封装的“StandardAction”这些标准操作,可以非常方便我们调用Android系统功能。在Android上会存在各类权限问题造成应用无法运行创建工程 File->New->Multi-DeviceApplication-Delphi选择" BlankApplication",点击"OK"完成......
  • Android 实现加减自定义控件
    ✍️作者简介:沫小北/码农小北(专注于Android、Web、TCP/IP等技术方向)</br>......
  • 2023-11-08 Android studio下载的模拟器存放路径以及如何修改存放路径 ==》默认路径:C:
    模拟器存放默认路径:C:\Users\Administrator\.android\avd如何修改:设置一个系统变量,如图,点击Help==》EditCustomProperties 然后再弹出的文本框里输入你要存放的路径,比如我存在D盘的adv文件夹里面ANDROID_AVD_HOME=D:\\adv 我的as版本:2022.3.1Patch3 写到最后:c盘......
  • Android.mk 笔记
    相关函数makefile文件里的函数跟变量的使用方法很相似,都是用一个$符号跟左括号,函数名,空格后跟一列由逗号分隔的参数,最后用右括号回括(1)strip函数名称:去空格函数-strip功能:去掉<string>字串中开头和结尾的空字符串,并将中间的多个连续空字符(如果有的化)合并未一个空字符。返回......
  • android短视频开发,uniapp页面滚动条到指定位置
    android短视频开发,uniapp页面滚动条到指定位置#html指定位置<viewclass="gap_body_position"></view> #js执行this.$nextTick(()=>{  //一定要用nextTickuni.pageScrollTo({duration:300,selector:'.gap_body_position'});})​以上就是android短视频开发,uniapp页......
  • android 12 修改Launcher3 app hotseat 图标形状为圆角图标
    1.概述在对11.0产品开发中,对于Launcher3做各种定制化开发,也是常见的,最近有功能需求要求,对于修改图标的形状为圆角图标,而在Launcher3中,所有的app和hotseat都是由BubbleTextView负责构建的,所以对于图标的修改也是要从BubbleTextView.java修改的在这里插入图片描述2.修改Launcher......
  • 从零开始构建报警中心:part04 钉钉消息-webhook
    现在工作上比较常用的IM一般式钉钉企微飞书,其实使用起来都是大同小异的。这里就用钉钉来实现。使用钉钉发送信息,一般有三种形式群webhook工作通知智能机器人智能机器人方式,能实现一定的交互功能,但逻辑相对复杂,这里只是需要一个实时的钉钉消息,所以不进行讨论。添加群webhook这是一......
  • 标题:计算机音视频技术的发展与应用
    音视频技术是计算机领域中备受关注的研究方向之一,近年来随着计算机性能的不断提升和网络带宽的增加,音视频技术在通讯、娱乐、教育等领域得到了广泛的应用。本文将就计算机音视频技术的发展历程、基本原理及应用进行探讨。###1.计算机音视频技术的发展历程计算机音视频技术起源于......