欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android使用AudioRecord和AudioTrack完成音频的采集和播放以及使用MediaCodec完成硬编和硬解

程序员文章站 2022-03-22 23:49:20
一、概述音视频的学习在弄清楚了一些基本概念后,接下来就是要会使用系统提供的一些API,通过实际的应用能更好的帮助我们理解其中的原理。二、音频的采集这里我们使用AudioRecord,因为它更接近系统底层,灵活性也更高,能获取到原始的PCM数据。下面来看一下采集的流程:1)获取权限2)获取AudioRecord所需的缓冲区3)创建AudioRecord对象4)创建保存录制文件夹5)开始采集6)停止采集并释放AudioRecord对象1、获取权限<.....

一、概述

音视频的学习在弄清楚了一些基本概念后,接下来就是要会使用系统提供的一些API,通过实际的应用能更好的帮助我们理解其中的原理。

 

二、音频的采集

这里我们使用AudioRecord,因为它更接近系统底层,灵活性也更高,能获取到原始的PCM数据。下面来看一下采集的流程:

1)获取权限

2)获取AudioRecord所需的缓冲区

3)创建AudioRecord对象

4)创建保存录制文件夹

5)开始采集

6)停止采集并释放AudioRecord对象

 

1、获取权限

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

此处向系统申请了录音和存储权限,5.0以上系统还要在代码上动态申请。

2、获取AudioRecord所需的缓冲区

//创建AudioRecord对象所需的最小缓冲区大小
final int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE_HZ, CHANNEL_IN_CONFIG, AUDIO_FORMAT);

这里要通过调用系统API获取,而不是自己随便设置,参数就是系统采集用到的一些参数。

3、创建AudioRecord对象

//创建AudioRecord对象
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE_HZ, CHANNEL_IN_CONFIG, AUDIO_FORMAT, minBufferSize);

创建完AudioRecord对象对象后就是进行音频的采集了,具体的代码会在文章最后贴出来。

 

三、音频的播放

这里使用AudioTrack来完成音频播放,流程如下:

1)获取播放源的路径

2)获取AudioTrack所需的缓冲区

3)创建AudioTrack对象

4)开始播放

5)停止播放并释放AudioTrack对象

 

1、获取播放源就是找到之前采集的PCM数据

2、获取AudioTrack所需的缓冲区

//创建AudioTrack对象所需的估计最小缓冲区大小
final int minBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE_HZ, CHANNEL_OUT_CONFIG, AUDIO_FORMAT);

这里跟获取AudioRecord所需的缓冲区类似

3、创建AudioTrack对象

//创建AudioTrack对象
audioTrack = new AudioTrack(new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build(),
                new AudioFormat.Builder().setSampleRate(SAMPLE_RATE_HZ)
                        .setEncoding(AUDIO_FORMAT)
                        .setChannelMask(CHANNEL_OUT_CONFIG)
                        .build(),
                minBufferSize, mode, AudioManager.AUDIO_SESSION_ID_GENERATE);

里面涉及到的一些参数具体可以看代码,接下来就可以进行音频播放了。

 

四、使用MediaCodec硬编

MediaCodec是一个比较重要的类,从Android 4.1开始推出,主要用于音视频的硬编和硬解码。相对于软编和软解通过CPU进行编解码,其主要是利用自身的硬件来完成。所以效率更高,但也有缺点,就是适配性比较差,因为市面上的机型太多了,每个厂商又会对底层进行修改。从长远来来看还是比较看好MediaCodec

下面来看一张官方给的MediaCodec工作流程图

Android使用AudioRecord和AudioTrack完成音频的采集和播放以及使用MediaCodec完成硬编和硬解

所有的编解码步骤都是围绕这张图的流程来做的,这里做一个大概的介绍:

1)整理看可以分为三部分,第一部分是数据输入(input),第二部分是编解码(Codec),最后是数据输出(output)

2)数据输入

a、获取输入缓冲区:int inputIndex = MediaCodec.dequeueInputBuffer(long timeoutUs),用于将需要处理的数据存在里面。

b、获取ByteBuffer对象,并向输入缓冲区添加数据:ByteBuffer inputByteBuffer = MediaCodec.getInputBuffer(inputIndex)

      inputByteBuffer.clear();

      inputByteBuffer.put(data)

c、把添加了数据的输入缓冲区送到Codec里面进行处理:MediaCodec.queueInputBuffer(inputIndex, 0, data.length, 0, 0)

3)数据输出

a、获取输出缓冲区:int outputIndex = MediaCodec.dequeueOutputBuffer(BufferInfo info, long timeoutUs),BufferInfo包含每     个buffer元素的信息

b、获取到处理后的数据:ByteBuffer outputByteBuffer = MediaCodec.getOutputBuffer(outputIndex)

c、释放输出缓冲区:MediaCodec.releaseOutputBuffer(outputIndex, false)

以上大概讲了一下MediaCodec的使用流程,下面来看下编码的流程:

1、初始化编码器并设置相关参数

/**
 * 初始化编码器
 */
public void initEncoder() {
        try {
            //创建文件夹
            File fileFolder = new   File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + FOLDER_NAME);
            if (!fileFolder.exists()) {
                fileFolder.mkdir();
            }
            String fileFolderPath = fileFolder.getAbsolutePath();
            final File file = new File(fileFolderPath + "/" + ENCODER_FILE + ".aac");
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            mFileOutputStream = new FileOutputStream(file.getAbsoluteFile());

            //设置编码参数
            MediaFormat mediaFormat = MediaFormat.createAudioFormat(MINE_TYPE_AAC, SAMPLE_RATE_HZ, CHANNEL_COUNT);
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
            mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, MAX_BUFFER_SIZE);
            //根据类型实例化一个编码器
            mMediaCodec = MediaCodec.createEncoderByType(MINE_TYPE_AAC);
            //MediaCodec.CONFIGURE_FLAG_ENCODE 表示需要配置一个编码器,而不是解码器
            mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            if (mMediaCodec == null) {
                Log.e(TAG, "Create media encode failed");
                return;
            }
            //start()后进入执行状态,才能做后续的操作
            mMediaCodec.start();
            //解码后的数据,包含每一个buffer的元数据信息
            mBufferInfo = new MediaCodec.BufferInfo();
            Log.i(TAG, "Create media encode succeed");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2、编码

/**
 * 编码
 *
 * @param data
 */
public void encodeData(byte[] data) {
        //dequeueInputBuffer(time)需要传入一个时间值,-1表示一直等待,0表示不等待有可能会丢帧,其他表示等待多少毫秒
        //获取输入缓存的index
        int inputIndex = mMediaCodec.dequeueInputBuffer(-1);
        if (inputIndex >= 0) {
            ByteBuffer inputByteBuffer = mMediaCodec.getInputBuffer(inputIndex);
            inputByteBuffer.clear();
            //添加数据
            inputByteBuffer.put(data);
            //限制ByteBuffer的访问长度
            inputByteBuffer.limit(data.length);
            //把输入缓存塞回去给MediaCodec
            mMediaCodec.queueInputBuffer(inputIndex, 0, data.length, 0, 0);
        }

        //获取输出缓存的index
        int outputIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 0);
        while (outputIndex >= 0) {
            //获取缓存信息的长度
            int byteBufSize = mBufferInfo.size;
            //添加ADTS头部后的长度
            int bytePacketSize = byteBufSize + 7;

            ByteBuffer outByteBuffer = mMediaCodec.getOutputBuffer(outputIndex);
            outByteBuffer.position(mBufferInfo.offset);
            outByteBuffer.limit(mBufferInfo.offset + mBufferInfo.size);

            byte[] targetByte = new byte[bytePacketSize];
            //添加ADTS头部
            addADTStoPacket(targetByte, bytePacketSize);
            //将编码得到的AAC数据 取出到byte[]中 偏移量offset=7
            outByteBuffer.get(targetByte, 7, byteBufSize);

            outByteBuffer.position(mBufferInfo.offset);

            try {
                mFileOutputStream.write(targetByte);
            } catch (IOException e) {
                e.printStackTrace();
            }
            //释放
            mMediaCodec.releaseOutputBuffer(outputIndex, false);
            outputIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 0);
        }
    }

 3、停止编码

/**
 * 停止编码
 */
public void stopEncode() {
    if (mMediaCodec != null) {
        mMediaCodec.stop();
        mMediaCodec.release();
        Log.i(TAG, "Stop encode");
    }
}

 至此硬编就完成了,硬解的流程也是类似,这里就不说了,具体可以参照代码。

AudioDemo

本文地址:https://blog.csdn.net/zhongfangguiyishi/article/details/107287825