简体   繁体   English

Android MediaCodec AAC编码器

[英]Android MediaCodec AAC encoder

I use the MediaCodec class provided by the Android SDK since API level 16 with the OMX.SEC.aac.enc encoder to encode audio to a file. I use the MediaCodec class provided by the Android SDK since API level 16 with the OMX.SEC.aac.enc encoder to encode audio to a file. I get the audio input from the AudioRecord class.我从AudioRecord获得音频输入。 My instance of the AudioRecord class is configured like this:我的AudioRecord实例配置如下:

bufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_DEFAULT, bufferSize);

I am able to play the raw data from the AudioRecord instance, so the problem does not reside there.我能够从AudioRecord实例播放原始数据,因此问题不存在。

I write the output from the AudioRecord instance to a ByteBuffer instance and pass it to an available input buffer from the encoder.我将 output 从AudioRecord实例写入ByteBuffer实例,并将其从编码器传递到可用的输入缓冲区。 The output from the encoder is written to a file on the SD-card.来自编码器的 output 被写入 SD 卡上的文件。

These are the configuration parameters for my MediaCodec instance:这些是我的MediaCodec实例的配置参数:

codec = MediaCodec.createEncoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

VLC tells me that there are no streams in my aac file. VLC 告诉我我的 aac 文件中没有流。 The command FFMPEG -i @filename@ gives me the following error: Invalid data found when processing input .命令FFMPEG -i @filename@给我以下错误: Invalid data found when processing input None of the mediaplayers I tested are able to play my file.我测试的所有媒体播放器都无法播放我的文件。

Why am I unable to play my file?为什么我无法播放我的文件? I receive no OpenMAX errors in LogCat and the application does not crash when encoding.我在LogCat中没有收到任何OpenMAX错误,并且应用程序在编码时不会崩溃。 I wrote a video encoder that works on the same principle and it works.我写了一个基于相同原理工作的视频编码器,它可以工作。

This is the code to read the data from the AudioRecord instance to a buffer:这是从AudioRecord实例读取数据到缓冲区的代码:

    new Thread() {
        public void run() {
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bufferSize);
            int read = 0;
            while (isRecording) {
                read = recorder.read(byteBuffer, bufferSize);
                if(AudioRecord.ERROR_INVALID_OPERATION != read){
                    encoder.add(byteBuffer);
                }
            }
            recorder.stop();
        }
    }.start();

The function add from my encoder copies the content of one buffer to another:从我的编码器添加的 function 将一个缓冲区的内容复制到另一个缓冲区:

public void add(ByteBuffer input) {
    if (!isRunning)
        return; 

    if (tmpInputBuffer == null)
        tmpInputBuffer = ByteBuffer.allocate(input.capacity());

    if (!tmpBufferClear)
        Log.e("audio encoder", "deadline missed"); //TODO lower bit rate

    synchronized (tmpInputBuffer) {
        tmpInputBuffer.clear();
        tmpInputBuffer.put(input);
        tmpInputBuffer.notifyAll();
        Log.d("audio encoder", "pushed data into tmpInputBuffer");
    }
}

The following code is used to occupy the input buffer of the encoder:以下代码用于占用编码器的输入缓冲区:

new Thread() {
    public void run() {
        while (isRunning) {
            if (tmpInputBuffer == null)
                continue;
            synchronized (tmpInputBuffer) {
                if (tmpBufferClear) {
                    try {
                        Log.d("audio encoder", "falling asleep");
                        tmpInputBuffer.wait(); //wait when no input is available
                    } 
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                ByteBuffer[] inputBuffers = codec.getInputBuffers();
                int inputBufferIndex;
                do
                    inputBufferIndex = codec.dequeueInputBuffer(-1);
                while (inputBufferIndex < 0);
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                inputBuffer.clear();
                Log.d("input buffer size", String.valueOf(inputBuffer.capacity()));
                Log.d("tmp input buffer size", String.valueOf(tmpInputBuffer.capacity()));
                inputBuffer.put(tmpInputBuffer.array());
                tmpInputBuffer.clear();
                codec.queueInputBuffer(inputBufferIndex, 0, tmpInputBuffer.capacity(), 0, 0);
                tmpBufferClear = true;
                Log.d("audio encoder", "added to input buffer");
            }
        }
    }
}.start();

I write the output from the encoder to a local file like this:我将 output 从编码器写入本地文件,如下所示:

    new Thread() {
        public void run() {
            while (isRunning) {
                ByteBuffer[] outputBuffers = codec.getOutputBuffers();
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, -1);
                while (outputBufferIndex >= 0) {
                    ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                    byte[] outData = new byte[bufferInfo.size];
                    outputBuffer.get(outData);

                    try {
                        fileWriter.write(outData, 0, outData.length);
                    } 
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                    codec.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 0);
                    Log.d("audio encoder", "removed from output buffer");
                }
            }
            codec.stop();

            try {
                fileWriter.close();
            } 
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }.start();
                tmpBufferClear = true;
                Log.d("audio encoder", "added to input buffer");
            }
        }
    }
}.start();

I guess you missed the MediaMuxer class.我猜你错过了MediaMuxer课程。 You need it if you want to write something got from MediaCodec to a file for example.例如,如果您想将从 MediaCodec 获取的内容写入文件,则需要它。

I had to solve the is use case as well.我也必须解决 is 用例。 It is possible, but there are some tricky things to get this working.这是可能的,但有一些棘手的事情要让它发挥作用。 I've upload my proof of concept app to GitHub.我已将我的概念验证应用程序上传到 GitHub。 It has a working example of how to do this.它有一个如何做到这一点的工作示例。

https://github.com/dburckh/EncodeRawAudio https://github.com/dburckh/EncodeRawAudio

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

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