使用MediaCodec,进行音频AAC硬编

本文详细介绍了如何使用Android的MediaCodec进行音频AAC硬编码,通过AudioRecorder录制PCM音频,然后利用MediaCodec将其编码成AAC格式。文章探讨了MediaCodec的工作原理,编码流程,并强调了添加ADTS头的重要性。同时,文中分享了录音和编码线程间数据同步的实现方式,以及在编码过程中可能遇到的问题和解决办法。
摘要由CSDN通过智能技术生成

与我们熟知的MP3格式一样,AAC是一种音频编码格式,对比MP3格式,AAC在缩小30%的前题下可以提供更好的音质。这篇博客的主要内容就是通过AudioRecorder录制PCM音频,再通过MediaCodec将PCM数据硬编码为AAC格式的音频。
通常我们使用MediaCodec的流程如下:

MediaCodec的使用流程:

  • createEncoderByType/createDecoderByType
  • configure
  • start
  • while(1) {
  • dequeueInputBuffer
  • queueInputBuffer
  • dequeueOutputBuffer
  • releaseOutputBuffer
  • }
  • stop
  • release

编解码器一个比较经典的工作原理图如下:
在这里插入图片描述
图中的Client一般就是我们开发者,解释一下就是:我们从Codec中拿到拿到空的input buffer,然后填充上我们需要进行编码的数据,再输送给Codec,Codec对数据进行编解码,编解码完成后,Codec将处理好的数据放进output buffer,我们取出后再清空返还给Codec,形成一个环形结构。可以看作一个生产者-消费者模式。

下面我们的编码流程也基本遵守上面的这个流程。为了便于大家理解,我画个流程图讲一下我整个代码的逻辑:
在这里插入图片描述

录音和编码分别在两个线程中进行,两个线程通过一个ArrayBlockQueue(这是一个线程安全的队列,想了解更多自己动手)队列共享数据,录音线程中的AudioRecorder通过read()将一帧数据put()到队尾,编码线程中的MediaCodec再通过take()取出队首的一帧数据进行编码。
首先看录音线程,为了使代码更加简洁易懂,我会省略掉一些代码,完整代码会在文末贴出。

    /**
     * 录音线程
     */
    public class AudioRecorder extends Thread {

        private AudioRecord mAudioRecord;
        private boolean isRecording;
        private int minBufferSize;

        public AudioRecorder() {
            isRecording = true;
            initRecorder();
        }

        @Override
        public void run() {
            super.run();
            startRecording();
        }

        /**
         * 初始化录音
         */
        public void initRecorder(){
            minBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
            mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRateInHz, channelConfig, audioFormat, minBufferSize);
            if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                isRecording = false;
                return;
            }
        }

        /**
         * 开始录音
         */
        public void startRecording(){
            if (mAudioRecord == null){
                return;
            }

            mAudioRecord.startRecording();
            while (isRecording) {
                //自定义的一个类,用来存储一帧pcm数据,即byte[],下面给出具体定义,很简单
                AudioDate audioDate = new AudioDate();
                audioDate.buffer = ByteBuffer.allocateDirect(minBufferSize);
                audioDate.size = mAudioRecord.read(audioDate.buffer, minBufferSize);
                try {
                    if (queue != null) {
                        queue.put(audioDate);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            release();
        }
    }

录音线程比较简单,主要是先初始化录音器在initRecorder()中,然后通过AudioRecorder的read方法,获取到一帧数据,通过queue.put放入队尾。
然后是编码线程。

/**
 * 音频编码线程
  */
public class AudioEncorder extends Thread {

    private MediaCodec mEncorder;
 private Boolean isEncording = false;
 private int minBufferSize;

 private OutputStream mFileStream;

 public AudioEncorder() {
        isEncording = true;
  initEncorder();
  }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值