简体   繁体   English

在Android上使用AudioRecord和MediaCodec编码AAC音频

[英]Encoding AAC Audio using AudioRecord and MediaCodec on Android

I am trying to encode aac audio using android AudioRecord and MediaCodec. 我正在尝试使用Android AudioRecord和MediaCodec编码aac音频。 I have created a encoder class very similar to ( Encoding H.264 from camera with Android MediaCodec ). 我创建了一个非常类似的编码器类( 使用Android MediaCodec从相机编码H.264 )。 With this class, I created an instance of AudioRecord and tell it to read off its byte[] data to the AudioEncoder (audioEncoder.offerEncoder(Data)). 通过这个类,我创建了一个AudioRecord实例,并告诉它将其byte []数据读出到AudioEncoder(audioEncoder.offerEncoder(Data))。

 while(isRecording) 
 {
  audioRecord.read(Data, 0, Data.length);
  audioEncoder.offerEncoder(Data);
 }

Here is my Setting for my AudioRecord 这是我的AudioRecord设置

    int audioSource = MediaRecorder.AudioSource.MIC;
    int sampleRateInHz = 44100;
    int channelConfig = AudioFormat.CHANNEL_IN_MONO;
    int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
    int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);

I successfully collected some byte[] array data and written it to a local file. 我成功收集了一些byte []数组数据并将其写入本地文件。 Unfortunately the file is not playable. 不幸的是,该文件无法播放。 I did some more searching online and found a related post ( How to generate the AAC ADTS elementary stream with Android MediaCodec ). 我做了一些在线搜索,发现了一个相关的帖子( 如何使用Android MediaCodec生成AAC ADTS基本流 )。 So, others who are having similar problem are saying the main problem is "The MediaCodec encoder generates the raw AAC stream. The raw AAC stream needs to be converted into a playable format, such as the ADTS stream". 因此,遇到类似问题的其他人说主要问题是“MediaCodec编码器生成原始AAC流。原始AAC流需要转换为可播放格式,例如ADTS流”。 So I tried to add the ADTS header. 所以我尝试添加ADTS标头。 Nevertheless, after I added the ADTS header(I commented out in the code below), my AudioEncoder wouldn't even write the output audio file. 然而,在我添加了ADTS标头后(我在下面的代码中注释掉了),我的AudioEncoder甚至都不会写输出音频文件。 Is there anything I'm missing? 有什么我想念的吗? Is my setup correct? 我的设置是否正确?

Any suggestions, comments, and opinions are welcome and very appreciated. 欢迎任何建议,意见和意见,非常感谢。 thanks guys! 多谢你们!

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Environment;
import android.util.Log;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class AudioEncoder {

    private MediaCodec mediaCodec;
    private BufferedOutputStream outputStream;
    private String mediaType = "audio/mp4a-latm";

    public AudioEncoder() {
        File f = new File(Environment.getExternalStorageDirectory(), "Download/audio_encoded.aac");
        touch(f);
        try {
            outputStream = new BufferedOutputStream(new FileOutputStream(f));
            Log.e("AudioEncoder", "outputStream initialized");
        } catch (Exception e){
            e.printStackTrace();
        }

        mediaCodec = MediaCodec.createEncoderByType(mediaType);
        final int kSampleRates[] = { 8000, 11025, 22050, 44100, 48000 };
        final int kBitRates[] = { 64000, 128000 };
        MediaFormat mediaFormat  = MediaFormat.createAudioFormat(mediaType,kSampleRates[3],1);
        mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);

        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates[1]);
        mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mediaCodec.start();
    }

    public void close() {
        try {
            mediaCodec.stop();
            mediaCodec.release();
            outputStream.flush();
            outputStream.close();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    // called AudioRecord's read
    public synchronized void offerEncoder(byte[] input) {
        Log.e("AudioEncoder", input.length + " is coming");

        try {
            ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
            ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
            int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                inputBuffer.clear();

                inputBuffer.put(input);


                mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0);
            }

            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);

////trying to add a ADTS
//            while (outputBufferIndex >= 0) {
//                int outBitsSize   = bufferInfo.size;
//                int outPacketSize = outBitsSize + 7;    // 7 is ADTS size
//                ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
//
//                outputBuffer.position(bufferInfo.offset);
//                outputBuffer.limit(bufferInfo.offset + outBitsSize);
//
//                byte[] outData = new byte[outPacketSize];
//                addADTStoPacket(outData, outPacketSize);
//
//                outputBuffer.get(outData, 7, outBitsSize);
//                outputBuffer.position(bufferInfo.offset);
//
////                byte[] outData = new byte[bufferInfo.size];
//                outputStream.write(outData, 0, outData.length);
//                Log.e("AudioEncoder", outData.length + " bytes written");
//
//                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
//                outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
//
//            }


//Without ADTS header
            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                byte[] outData = new byte[bufferInfo.size];
                outputBuffer.get(outData);
                outputStream.write(outData, 0, outData.length);
                Log.e("AudioEncoder", outData.length + " bytes written");

                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);

            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

    }

    /**
     *  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.
     **/
    private void addADTStoPacket(byte[] packet, int packetLen) {
        int profile = 2;  //AAC LC
        //39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
        int freqIdx = 4;  //44.1KHz
        int chanCfg = 2;  //CPE

        // fill in ADTS data
        packet[0] = (byte)0xFF;
        packet[1] = (byte)0xF9;
        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 touch(File f)
    {
        try {
            if(!f.exists())
                f.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

You can use Android's MediaMuxer to package the raw streams created by MediaCodec into a .mp4 file. 您可以使用Android的MediaMuxer将MediaCodec创建的原始数据包打包成.mp4文件。 Bonus: AAC packets contained in a .mp4 don't require the ADTS header. 额外:.mp4中包含的AAC数据包不需要ADTS标头。

I've got a working example of this technique on Github . 在Github上有一个这个技术工作示例

Check "testEncoder" method here for how to use MediaCodec as Encoder properly. 检查“testEncoder”方法了解如何正确使用MediaCodec作为编码器。

after that In your code, 之后在你的代码中,

your input(audio recorder) is configured for single audio channel while your output(ADTS packet header) is set for two channels(chanCfg = 2). 您的输入(录音机)配置为单个音频通道,而您的输出(ADTS数据包标题)设置为两个通道(chanCfg = 2)。

also if you change your input samplerate (currently 44.1khz) you also have to change freqIdx flag in ADTS packet header. 此外,如果您更改输入采样率(当前为44.1khz),您还必须更改ADTS数据包标头中的freqIdx标志。 check this link for valid values. 检查此链接是否有效值。

And ADTS header profile flag is set to "AAC LC", you can also found this under MediaCodecInfo.CodecProfileLevel . 并且ADTS标头配置文件标志设置为“AAC LC”,您也可以在MediaCodecInfo.CodecProfileLevel下找到它。 you have set profile = 2 that is MediaCodecInfo.CodecProfileLevel.AACObjectLC 你设置了profile = 2,即MediaCodecInfo.CodecProfileLevel.AACObjectLC

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM