将原始PCM位保存为压缩音频AAC / AMR文件

时间:2016-01-02 15:50:33

标签: android encoding audiorecord

我正在编写一个使用AudioRecord录制用户音频的应用程序。我将原始PCM位保存到short[]数组(因为我的编码是16位PCM)。录制完成后,我想压缩此音频数据并将其保存到文件中。我想知道Android SDK中是否有一些高级API可以帮助我解决这个问题。

目前,我正在尝试使用MediaCodec并将其保存为AAC格式。首先,我使用以下代码

创建一个包含原始数据的ByteBuffer
// allocating the array with length 2 * length of the byte array because we are creating a
    // ByteBuffer.
    final ByteBuffer byteBuffer = ByteBuffer.allocate(mDataArray.length * 2);

    // putting value in the ByteBuffer ..
    for(short elem : mDataArray) {
        byteBuffer.putShort(elem);
    }

因为,我想支持API级别16,我想我必须使用已弃用的一组函数(在此link中使用使用缓冲区数组进行同步处理(不建议使用)) 。这是正确的前进方式吗?

我看到的另一个问题是如何将编码的缓冲区保存到文件中。

1 个答案:

答案 0 :(得分:0)

您可以使用非常简单的 MediaRecorder API,并且自 API 1 以来已得到不同程度的支持。

否则,已弃用的缓冲区数组与标准同步方法之间的差异实际上非常小。我和你的情况一样,想支持 API 16,但我不能使用 MediaRecorder。

我创建了填充和排空缓冲区的例程:

private void fillCodecInputBuffer(ByteBuffer inputBuffer, int bufferId) {
    ... code to fill input buffer ...
}

private void drainCodecOutputBuffer(ByteBuffer outputBuffer, int bufferId) {
    ... code to drain output buffer ...
}

...然后我从一个单独的例程中为棒棒糖前和棒棒糖后平台调用这些。通过这种方式,较新的平台可以更有效地编码(如果使用已弃用的方法,它们就不能)。

棒棒糖前:

ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();;
ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();

private void processEncoderKitKat() {
    /* uses old pre-LOLLIPOP (v5) method */
    final int ENCODER_TIMEOUT_US = 0; /* we poll only - was 10000 */
    int inputBufferId = encoder.dequeueInputBuffer(ENCODER_TIMEOUT_US);
    if (inputBufferId >= 0) {
        fillCodecInputBuffer(encoderInputBuffers[inputBufferId], inputBufferId);
    }
    int outputBufferId = encoder.dequeueOutputBuffer(encoderBuffer, ENCODER_TIMEOUT_US);
    if (outputBufferId >= 0) {
        drainCodecOutputBuffer(encoderOutputBuffers[outputBufferId], outputBufferId);
    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
        encoderOutputBuffers = encoder.getOutputBuffers();
    }
    if ((encoderBuffer.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0)
            isFullyEncoded = true;
}

...请注意,这个旧方法可能需要通知您输出缓冲区已更改,您需要重置数组以适应。

棒棒糖后:

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void processEncoderLollipop() {
    /* uses post LOLLIPOP (v5) method */
    final int ENCODER_TIMEOUT_US = 0;  /* we poll only */
    int inputBufferId = encoder.dequeueInputBuffer(ENCODER_TIMEOUT_US);
    if (inputBufferId >= 0) {
        fillCodecInputBuffer(encoder.getInputBuffer(inputBufferId), inputBufferId);
    }
    int outputBufferId = encoder.dequeueOutputBuffer(encoderBuffer, ENCODER_TIMEOUT_US);
    if (outputBufferId >= 0) {
        drainCodecOutputBuffer(encoder.getOutputBuffer(outputBufferId), outputBufferId);
    }
    if ((encoderBuffer.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0)
        isFullyEncoded = true;
}

我很确定您也可以构建(推荐的)异步方式来调用填充/排出例程,但我还没有尝试过。