Android AudioRecord录音实时pcm 编码为 aac 文件

实现类:

package com.ar;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.widget.Button;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;

public class AudioRecordManager {

    private final int sampleRateInHz = 44100;//44100 16000
    private final int channelConfig = AudioFormat.CHANNEL_IN_MONO;
    private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
    private String audioFilePath;
    private String tag="AudioRecordManager";

    private class AudioData {
        private ByteBuffer buffer;
        private int size;
    }

    private AudioRecorder mAudioRecorder;
    private AudioEncorder mAudioEncorder;

    private ArrayBlockingQueue queue;

    /**
     *
     * @param audioFilePath 文件名
     */
    public AudioRecordManager(String audioFilePath) {
        this.audioFilePath=audioFilePath;

        queue = new ArrayBlockingQueue<>(1024);
        mAudioRecorder = new AudioRecorder();
        mAudioEncorder = new AudioEncorder();
    }

    public void startRecord() {
        mAudioRecorder.start();
        mAudioEncorder.start();
    }

    public void stopRecord() {
        mAudioRecorder.stopRecording();
        mAudioEncorder.stopEncording();
    }

    /**
     * 录音线程
     */
    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)*2;// 乘以2 加大缓冲区,防止其他意外
            mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, minBufferSize);
            if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                isRecording = false;
                return;
            }
        }

        /**
         * 释放资源
         */
        public void release() {
            if (mAudioRecord != null && mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
                mAudioRecord.stop();
            }
        }

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

            mAudioRecord.startRecording();
            while (isRecording) {
                long a=System.currentTimeMillis();
                AudioData audioDate = new AudioData();
                audioDate.buffer = ByteBuffer.allocateDirect(minBufferSize);
                audioDate.size = mAudioRecord.read(audioDate.buffer, minBufferSize);
                try {
                    if (queue != null) {
                        queue.put(audioDate);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                long b=System.currentTimeMillis()-a;
                Log.i(tag,"录制耗时-毫秒=="+b);
            }
            release();
        }

        /**
         * 结束录音
         */
        public void stopRecording() {
            isRecording = false;
        }
    }

    /**
     * 音频编码线程
     */
    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)
        @Override
        public void run() {
            super.run();
            startEncording();
        }

        /**
         * 初始化编码器
         */
        private void initEncorder() {
            minBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)*2;// 乘以2 加大缓冲区,防止其他意外
            try {
                mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
            } catch (Exception e) {
                e.printStackTrace();
            }
            MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRateInHz, 1);
            format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
            format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            format.setInteger(MediaFormat.KEY_BIT_RATE, 96000);
            format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, minBufferSize * 4);
            mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        }

        /**
         * 开始编码
         */
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        public void startEncording() {
            if (mEncorder == null) {
                return;
            }

            mEncorder.start();
            try {
                mFileStream = new FileOutputStream(audioFilePath);
                MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
                AudioData audioData;
                while (isEncording) {
                    long a=System.currentTimeMillis();
                    // 从队列中取出录音的一帧音频数据
                    audioData = getAudioData();

                    if (audioData == null) {
                        continue;
                    }

                    // 取出InputBuffer,填充音频数据,然后输送到编码器进行编码
                    int inputBufferIndex = mEncorder.dequeueInputBuffer(0);
                    if (inputBufferIndex >= 0) {
                        ByteBuffer inputBuffer = mEncorder.getInputBuffer(inputBufferIndex);
                        inputBuffer.clear();
                        inputBuffer.put(audioData.buffer);
                        mEncorder.queueInputBuffer(inputBufferIndex, 0, audioData.size, System.nanoTime(), 0);
                    }

                    // 取出编码好的一帧音频数据,然后给这一帧添加ADTS头
                    int outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
                    while (outputBufferIndex >= 0) {

//                        int outBitsSize = mBufferInfo.size;
//                        int outPacketSize = outBitsSize + 7; // ADTS头部是7个字节
//                        ByteBuffer outputBuffer = mEncorder.getOutputBuffer(outputBufferIndex);
//                        outputBuffer.position(mBufferInfo.offset);
//                        outputBuffer.limit(mBufferInfo.offset + outBitsSize);
//                        byte[] outData = new byte[outPacketSize];
//                        addADTStoPacket(outData, outPacketSize);
//                        outputBuffer.get(outData, 7, outBitsSize);
//                        outputBuffer.position(mBufferInfo.offset);
//                        mFileStream.write(outData);


                        ByteBuffer outputBuffer = mEncorder.getOutputBuffer(outputBufferIndex);
                        int outBufferSize = outputBuffer.limit() + 7;
                        byte[] aacBytes = new byte[outBufferSize];
                        addADTStoPacket(aacBytes, outBufferSize);
                        outputBuffer.get(aacBytes, 7, outputBuffer.limit());
                        mFileStream.write(aacBytes);

                        mEncorder.releaseOutputBuffer(outputBufferIndex, false);
                        outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
                    }

                    long b=System.currentTimeMillis()-a;
                    Log.i(tag,"编码耗时-毫秒=="+b);
                }
                release();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 停止编码
         */
        public void stopEncording() {
            isEncording = false;
        }

        /**
         * 从队列中取出一帧待编码的音频数据
         *
         * @return
         */
        public AudioData getAudioData() {
            if (queue != null) {
                try {
                    return (AudioData) queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        /**
         *  Add ADTS header at the beginning of each and every AAC packet.
         *  This is needed as MediaCodec encoder generates a packet of raw
         *  AAC data.
         *
         *  Note the packetLen must count in the ADTS header itself !!! .
         *注意,这里的packetLen参数为raw aac Packet Len + 7; 7 bytes adts header
         **/
        private void addADTStoPacket(byte[] packet, int packetLen) {
            int profile = 2;  //AAC LC,MediaCodecInfo.CodecProfileLevel.AACObjectLC;
            int freqIdx = 4;  //见后面注释avpriv_mpeg4audio_sample_rates中32000对应的数组下标,来自ffmpeg源码
            int chanCfg = 1;  //见后面注释channel_configuration,AudioFormat.CHANNEL_IN_MONO 单声道(声道数量)

        /*int avpriv_mpeg4audio_sample_rates[] = {96000, 88200, 64000, 48000, 44100, 32000,24000, 22050, 16000, 12000, 11025, 8000, 7350};
        channel_configuration: 表示声道数chanCfg
        0: Defined in AOT Specifc Config
        1: 1 channel: front-center
        2: 2 channels: front-left, front-right
        3: 3 channels: front-center, front-left, front-right
        4: 4 channels: front-center, front-left, front-right, back-center
        5: 5 channels: front-center, front-left, front-right, back-left, back-right
        6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
        7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
        8-15: Reserved
        */

            // fill in ADTS data
            packet[0] = (byte)0xFF;
            //packet[1] = (byte)0xF9;
            packet[1] = (byte)0xF1;//解决ios 不能播放问题
            packet[2] = (byte)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
            packet[3] = (byte)(((chanCfg&3)<<6) + (packetLen>>11));
            packet[4] = (byte)((packetLen&0x7FF) >> 3);
            packet[5] = (byte)(((packetLen&7)<<5) + 0x1F);
            packet[6] = (byte)0xFC;
        }

        /**
         * 释放资源
         */
        public void release() {
            if (mFileStream != null) {
                try {
                    mFileStream.flush();
                    mFileStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (mEncorder != null) {
                mEncorder.stop();
            }
        }
    }

}

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值